r3302: Grab all combos of lock keys.
[rox-filer.git] / ROX-Filer / src / dir.c
blob918e89288df660b33566b4c0f057a285b14b94ec
1 /*
2 * $Id$
4 * Copyright (C) 2003, 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 * - The recheck list is replaced with this new list.
41 * This system is designed to get the number of items and their names quickly,
42 * so that the auto-sizer can make a good guess.
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 GFSCache *dir_cache = NULL;
83 /* Static prototypes */
84 static void update(Directory *dir, gchar *pathname, gpointer data);
85 static void set_idle_callback(Directory *dir);
86 static DirItem *insert_item(Directory *dir, const guchar *leafname);
87 static void remove_missing(Directory *dir, GPtrArray *keep);
88 static void dir_recheck(Directory *dir,
89 const guchar *path, const guchar *leafname);
90 static GPtrArray *hash_to_array(GHashTable *hash);
91 static void dir_force_update_item(Directory *dir, const gchar *leaf);
92 static Directory *dir_new(const char *pathname);
93 static void dir_rescan(Directory *dir);
94 #ifdef USE_DNOTIFY
95 static void dir_rescan_soon(Directory *dir);
96 static void dnotify_handler(int sig, siginfo_t *si, void *data);
97 #endif
99 /****************************************************************
100 * EXTERNAL INTERFACE *
101 ****************************************************************/
103 void dir_init(void)
105 dir_cache = g_fscache_new((GFSLoadFunc) dir_new,
106 (GFSUpdateFunc) update, NULL);
108 /* Check for dnotify support in the kernel */
109 #ifdef USE_DNOTIFY
111 struct sigaction act;
113 act.sa_sigaction = dnotify_handler;
114 sigemptyset(&act.sa_mask);
115 act.sa_flags = SA_SIGINFO;
116 sigaction(SIGRTMIN, &act, NULL);
118 /* Sometimes we get this instead of SIGRTMIN.
119 * Don't know why :-( but don't crash...
121 act.sa_handler = SIG_IGN;
122 sigemptyset(&act.sa_mask);
123 act.sa_flags = 0;
124 sigaction(SIGIO, &act, NULL);
126 dnotify_fd_to_dir = g_hash_table_new(NULL, NULL);
128 #endif
131 /* Periodically calls callback to notify about changes to the contents
132 * of the directory.
133 * Before this function returns, it calls the callback once to add all
134 * the items currently in the directory (unless the dir is empty).
135 * If we are not scanning, it also calls update(DIR_END_SCAN).
137 void dir_attach(Directory *dir, DirCallback callback, gpointer data)
139 DirUser *user;
140 GPtrArray *items;
142 g_return_if_fail(dir != NULL);
143 g_return_if_fail(callback != NULL);
145 user = g_new(DirUser, 1);
146 user->callback = callback;
147 user->data = data;
149 #ifdef USE_DNOTIFY
150 if (!dir->users)
152 int fd;
154 if (dir->dnotify_fd != -1)
155 g_warning("dir_attach: dnotify error\n");
157 fd = open(dir->pathname, O_RDONLY);
158 g_return_if_fail(g_hash_table_lookup(dnotify_fd_to_dir,
159 GINT_TO_POINTER(fd)) == NULL);
160 if (fd != -1)
162 dir->dnotify_fd = fd;
163 g_hash_table_insert(dnotify_fd_to_dir,
164 GINT_TO_POINTER(fd), dir);
165 fcntl(fd, F_SETSIG, SIGRTMIN);
166 fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME |
167 DN_ATTRIB | DN_MULTISHOT);
170 #endif
172 dir->users = g_list_prepend(dir->users, user);
174 g_object_ref(dir);
176 items = hash_to_array(dir->known_items);
177 if (items->len)
178 callback(dir, DIR_ADD, items, data);
179 g_ptr_array_free(items, TRUE);
181 if (dir->needs_update && !dir->scanning)
182 dir_rescan(dir);
184 /* May start scanning if noone was watching before */
185 set_idle_callback(dir);
187 if (!dir->scanning)
188 callback(dir, DIR_END_SCAN, NULL, data);
191 /* Undo the effect of dir_attach */
192 void dir_detach(Directory *dir, DirCallback callback, gpointer data)
194 DirUser *user;
195 GList *list;
197 g_return_if_fail(dir != NULL);
198 g_return_if_fail(callback != NULL);
200 for (list = dir->users; list; list = list->next)
202 user = (DirUser *) list->data;
203 if (user->callback == callback && user->data == data)
205 g_free(user);
206 dir->users = g_list_remove(dir->users, user);
207 g_object_unref(dir);
209 /* May stop scanning if noone's watching */
210 set_idle_callback(dir);
212 #ifdef USE_DNOTIFY
213 if (!dir->users && dir->dnotify_fd != -1)
215 close(dir->dnotify_fd);
216 g_hash_table_remove(dnotify_fd_to_dir,
217 GINT_TO_POINTER(dir->dnotify_fd));
218 dir->dnotify_fd = -1;
220 #endif
221 return;
225 g_warning("dir_detach: Callback/data pair not attached!\n");
228 void dir_update(Directory *dir, gchar *pathname)
230 update(dir, pathname, NULL);
233 /* Rescan this directory */
234 void refresh_dirs(const char *path)
236 g_fscache_update(dir_cache, path);
239 /* When something has happened to a particular object, call this
240 * and all appropriate changes will be made.
242 void dir_check_this(const guchar *path)
244 guchar *real_path;
245 guchar *dir_path;
246 Directory *dir;
248 dir_path = g_path_get_dirname(path);
249 real_path = pathdup(dir_path);
250 g_free(dir_path);
252 dir = g_fscache_lookup_full(dir_cache, real_path,
253 FSCACHE_LOOKUP_PEEK, NULL);
254 if (dir)
256 dir_recheck(dir, real_path, g_basename(path));
257 g_object_unref(dir);
260 g_free(real_path);
263 #ifdef USE_DNOTIFY
264 static void drop_dnotify(gpointer key, gpointer value, gpointer data)
266 close(GPOINTER_TO_INT(key));
268 #endif
270 /* Used when we fork an action child, otherwise we can't delete or unmount
271 * any directory which we're watching!
273 void dir_drop_all_dnotifies(void)
275 #ifdef USE_DNOTIFY
276 g_hash_table_foreach(dnotify_fd_to_dir, drop_dnotify, NULL);
277 #endif
280 /* Tell watchers that this item has changed, but don't rescan.
281 * (used when thumbnail has been created for an item)
283 void dir_force_update_path(const gchar *path)
285 gchar *dir_path;
286 Directory *dir;
288 g_return_if_fail(path[0] == '/');
290 dir_path = g_path_get_dirname(path);
292 dir = g_fscache_lookup_full(dir_cache, dir_path, FSCACHE_LOOKUP_PEEK,
293 NULL);
294 if (dir)
296 dir_force_update_item(dir, g_basename(path));
297 g_object_unref(dir);
300 g_free(dir_path);
303 /* Ensure that 'leafname' is up-to-date. Returns the new/updated
304 * DirItem, or NULL if the file no longer exists.
306 DirItem *dir_update_item(Directory *dir, const gchar *leafname)
308 DirItem *item;
310 time(&diritem_recent_time);
311 item = insert_item(dir, leafname);
312 dir_merge_new(dir);
314 return item;
317 static int sort_names(const void *a, const void *b)
319 return strcmp(*((char **) a), *((char **) b));
322 static void free_recheck_list(Directory *dir)
324 destroy_glist(&dir->recheck_list);
327 /* If scanning state has changed then notify all filer windows */
328 static void dir_set_scanning(Directory *dir, gboolean scanning)
330 GList *next;
332 if (scanning == dir->scanning)
333 return;
335 dir->scanning = scanning;
337 for (next = dir->users; next; next = next->next)
339 DirUser *user = (DirUser *) next->data;
341 user->callback(dir,
342 scanning ? DIR_START_SCAN : DIR_END_SCAN,
343 NULL, user->data);
347 /* Notify everyone that the error status of the directory has changed */
348 static void dir_error_changed(Directory *dir)
350 GList *next;
352 for (next = dir->users; next; next = next->next)
354 DirUser *user = (DirUser *) next->data;
356 user->callback(dir, DIR_ERROR_CHANGED, NULL, user->data);
360 /* This is called in the background when there are items on the
361 * dir->recheck_list to process.
363 static gboolean recheck_callback(gpointer data)
365 Directory *dir = (Directory *) data;
366 GList *next;
367 guchar *leaf;
369 g_return_val_if_fail(dir != NULL, FALSE);
370 g_return_val_if_fail(dir->recheck_list != NULL, FALSE);
372 /* Remove the first name from the list */
373 next = dir->recheck_list;
374 dir->recheck_list = g_list_remove_link(dir->recheck_list, next);
375 leaf = (guchar *) next->data;
376 g_list_free_1(next);
378 /* usleep(800); */
380 insert_item(dir, leaf);
382 g_free(leaf);
384 if (dir->recheck_list)
385 return TRUE; /* Call again */
387 /* The recheck_list list empty. Stop scanning, unless
388 * needs_update, in which case we start scanning again.
391 dir_merge_new(dir);
393 dir->have_scanned = TRUE;
394 dir_set_scanning(dir, FALSE);
395 gtk_idle_remove(dir->idle_callback);
396 dir->idle_callback = 0;
398 if (dir->needs_update)
399 dir_rescan(dir);
401 return FALSE;
404 /* Add all the new items to the items array.
405 * Notify everyone who is watching us.
407 void dir_merge_new(Directory *dir)
409 GPtrArray *new = dir->new_items;
410 GPtrArray *up = dir->up_items;
411 GPtrArray *gone = dir->gone_items;
412 GList *list;
413 guint i;
415 for (list = dir->users; list; list = list->next)
417 DirUser *user = (DirUser *) list->data;
419 if (new->len)
420 user->callback(dir, DIR_ADD, new, user->data);
421 if (up->len)
422 user->callback(dir, DIR_UPDATE, up, user->data);
423 if (gone->len)
424 user->callback(dir, DIR_REMOVE, gone, user->data);
427 for (i = 0; i < new->len; i++)
429 DirItem *item = (DirItem *) new->pdata[i];
431 g_hash_table_insert(dir->known_items, item->leafname, item);
434 for (i = 0; i < gone->len; i++)
436 DirItem *item = (DirItem *) gone->pdata[i];
438 diritem_free(item);
441 g_ptr_array_set_size(gone, 0);
442 g_ptr_array_set_size(new, 0);
443 g_ptr_array_set_size(up, 0);
446 #ifdef USE_DNOTIFY
447 /* Called from the mainloop shortly after dnotify_handler */
448 void dnotify_wakeup(void)
450 Directory *dir;
452 dnotify_wakeup_flag = FALSE;
454 dir = g_hash_table_lookup(dnotify_fd_to_dir,
455 GINT_TO_POINTER(dnotify_last_fd));
457 if (dir)
458 dir_rescan_soon(dir);
460 #endif
462 /****************************************************************
463 * INTERNAL FUNCTIONS *
464 ****************************************************************/
466 #ifdef USE_DNOTIFY
467 static gint rescan_soon_timeout(gpointer data)
469 Directory *dir = (Directory *) data;
471 dir->rescan_timeout = -1;
472 if (dir->scanning)
473 dir->needs_update = TRUE;
474 else
475 dir_rescan(dir);
476 return FALSE;
479 /* Wait a fraction of a second and then rescan. If already waiting,
480 * this function does nothing.
482 static void dir_rescan_soon(Directory *dir)
484 if (dir->rescan_timeout != -1)
485 return;
486 dir->rescan_timeout = gtk_timeout_add(500, rescan_soon_timeout, dir);
488 #endif
490 static void free_items_array(GPtrArray *array)
492 guint i;
494 for (i = 0; i < array->len; i++)
496 DirItem *item = (DirItem *) array->pdata[i];
498 diritem_free(item);
501 g_ptr_array_free(array, TRUE);
504 /* Tell everyone watching that these items have gone */
505 static void notify_deleted(Directory *dir, GPtrArray *deleted)
507 GList *next;
509 if (!deleted->len)
510 return;
512 for (next = dir->users; next; next = next->next)
514 DirUser *user = (DirUser *) next->data;
516 user->callback(dir, DIR_REMOVE, deleted, user->data);
520 static void mark_unused(gpointer key, gpointer value, gpointer data)
522 DirItem *item = (DirItem *) value;
524 item->may_delete = TRUE;
527 static void keep_deleted(gpointer key, gpointer value, gpointer data)
529 DirItem *item = (DirItem *) value;
530 GPtrArray *deleted = (GPtrArray *) data;
532 if (item->may_delete)
533 g_ptr_array_add(deleted, item);
536 static gboolean check_unused(gpointer key, gpointer value, gpointer data)
538 DirItem *item = (DirItem *) value;
540 return item->may_delete;
543 /* Remove all the old items that have gone.
544 * Notify everyone who is watching us of the removed items.
546 static void remove_missing(Directory *dir, GPtrArray *keep)
548 GPtrArray *deleted;
549 guint i;
551 deleted = g_ptr_array_new();
553 /* Mark all current items as may_delete */
554 g_hash_table_foreach(dir->known_items, mark_unused, NULL);
556 /* Unmark all items also in 'keep' */
557 for (i = 0; i < keep->len; i++)
559 guchar *leaf = (guchar *) keep->pdata[i];
560 DirItem *item;
562 item = g_hash_table_lookup(dir->known_items, leaf);
564 if (item)
565 item->may_delete = FALSE;
568 /* Add each item still marked to 'deleted' */
569 g_hash_table_foreach(dir->known_items, keep_deleted, deleted);
571 /* Remove all items still marked */
572 g_hash_table_foreach_remove(dir->known_items, check_unused, NULL);
574 notify_deleted(dir, deleted);
576 free_items_array(deleted);
579 static gint notify_timeout(gpointer data)
581 Directory *dir = (Directory *) data;
583 g_return_val_if_fail(dir->notify_active == TRUE, FALSE);
585 dir_merge_new(dir);
587 dir->notify_active = FALSE;
588 g_object_unref(dir);
590 return FALSE;
593 /* Call dir_merge_new() after a while. */
594 static void delayed_notify(Directory *dir)
596 if (dir->notify_active)
597 return;
598 g_object_ref(dir);
599 gtk_timeout_add(1500, notify_timeout, dir);
600 dir->notify_active = TRUE;
603 /* Stat this item and add, update or remove it.
604 * Returns the new/updated item, if any.
605 * (leafname may be from the current DirItem item)
606 * Ensure diritem_recent_time is reasonably up-to-date before calling this.
608 static DirItem *insert_item(Directory *dir, const guchar *leafname)
610 const gchar *full_path;
611 DirItem *item;
612 DirItem old;
613 gboolean do_compare = FALSE; /* (old is filled in) */
615 if (leafname[0] == '.' && (leafname[1] == '\n' ||
616 (leafname[1] == '.' && leafname[2] == '\n')))
617 return NULL; /* Ignore '.' and '..' */
619 full_path = make_path(dir->pathname, leafname);
620 item = g_hash_table_lookup(dir->known_items, leafname);
622 if (item)
624 if (item->base_type != TYPE_UNKNOWN)
626 /* Preserve the old details so we can compare */
627 old = *item;
628 if (old.image)
629 g_object_ref(old.image);
630 do_compare = TRUE;
632 diritem_restat(full_path, item, &dir->stat_info);
634 else
636 /* Item isn't already here. This won't normally happen,
637 * because blank items are added when scanning, before
638 * we get here.
640 item = diritem_new(leafname);
641 diritem_restat(full_path, item, &dir->stat_info);
642 if (item->base_type == TYPE_ERROR &&
643 item->lstat_errno == ENOENT)
645 diritem_free(item);
646 return NULL;
648 g_ptr_array_add(dir->new_items, item);
652 if (item->base_type == TYPE_ERROR && item->lstat_errno == ENOENT)
654 /* Item has been deleted */
655 g_hash_table_remove(dir->known_items, item->leafname);
656 g_ptr_array_add(dir->gone_items, item);
657 if (do_compare && old.image)
658 g_object_unref(old.image);
659 delayed_notify(dir);
660 return NULL;
663 if (do_compare)
665 if (item->lstat_errno == old.lstat_errno
666 && item->base_type == old.base_type
667 && item->flags == old.flags
668 && item->size == old.size
669 && item->mode == old.mode
670 && item->atime == old.atime
671 && item->ctime == old.ctime
672 && item->mtime == old.mtime
673 && item->uid == old.uid
674 && item->gid == old.gid
675 && item->image == old.image
676 && item->mime_type == old.mime_type)
678 if (old.image)
679 g_object_unref(old.image);
680 return item;
682 if (old.image)
683 g_object_unref(old.image);
686 g_ptr_array_add(dir->up_items, item);
687 delayed_notify(dir);
689 return item;
692 static void update(Directory *dir, gchar *pathname, gpointer data)
694 g_free(dir->pathname);
695 dir->pathname = pathdup(pathname);
697 if (dir->scanning)
698 dir->needs_update = TRUE;
699 else
700 dir_rescan(dir);
703 /* If there is work to do, set the idle callback.
704 * Otherwise, stop scanning and unset the idle callback.
706 static void set_idle_callback(Directory *dir)
708 if (dir->recheck_list && dir->users)
710 /* Work to do, and someone's watching */
711 dir_set_scanning(dir, TRUE);
712 if (dir->idle_callback)
713 return;
714 time(&diritem_recent_time);
715 dir->idle_callback = gtk_idle_add(recheck_callback, dir);
716 /* Do the first call now (will remove the callback itself) */
717 recheck_callback(dir);
719 else
721 dir_set_scanning(dir, FALSE);
722 if (dir->idle_callback)
724 gtk_idle_remove(dir->idle_callback);
725 dir->idle_callback = 0;
730 /* See dir_force_update_path() */
731 static void dir_force_update_item(Directory *dir, const gchar *leaf)
733 GList *list;
734 GPtrArray *items;
735 DirItem *item;
737 items = g_ptr_array_new();
739 item = g_hash_table_lookup(dir->known_items, leaf);
740 if (!item)
741 goto out;
743 g_ptr_array_add(items, item);
745 for (list = dir->users; list; list = list->next)
747 DirUser *user = (DirUser *) list->data;
749 user->callback(dir, DIR_UPDATE, items, user->data);
752 out:
753 g_ptr_array_free(items, TRUE);
756 static void dir_recheck(Directory *dir,
757 const guchar *path, const guchar *leafname)
759 guchar *old = dir->pathname;
761 dir->pathname = g_strdup(path);
762 g_free(old);
764 time(&diritem_recent_time);
765 insert_item(dir, leafname);
768 static void to_array(gpointer key, gpointer value, gpointer data)
770 GPtrArray *array = (GPtrArray *) data;
772 g_ptr_array_add(array, value);
775 /* Convert a hash table to an unsorted GPtrArray.
776 * g_ptr_array_free() the result.
778 static GPtrArray *hash_to_array(GHashTable *hash)
780 GPtrArray *array;
782 array = g_ptr_array_new();
784 g_hash_table_foreach(hash, to_array, array);
786 return array;
789 static gpointer parent_class;
791 /* Note: dir_cache is never purged, so this shouldn't get called */
792 static void dir_finialize(GObject *object)
794 GPtrArray *items;
795 Directory *dir = (Directory *) object;
797 g_return_if_fail(dir->users == NULL);
799 g_print("[ dir finalize ]\n");
801 free_recheck_list(dir);
802 set_idle_callback(dir);
803 if (dir->rescan_timeout != -1)
804 gtk_timeout_remove(dir->rescan_timeout);
806 dir_merge_new(dir); /* Ensures new, up and gone are empty */
808 g_ptr_array_free(dir->up_items, TRUE);
809 g_ptr_array_free(dir->new_items, TRUE);
810 g_ptr_array_free(dir->gone_items, TRUE);
812 items = hash_to_array(dir->known_items);
813 free_items_array(items);
814 g_hash_table_destroy(dir->known_items);
816 g_free(dir->error);
817 g_free(dir->pathname);
819 G_OBJECT_CLASS(parent_class)->finalize(object);
822 static void directory_class_init(gpointer gclass, gpointer data)
824 GObjectClass *object = (GObjectClass *) gclass;
826 parent_class = g_type_class_peek_parent(gclass);
828 object->finalize = dir_finialize;
831 static void directory_init(GTypeInstance *object, gpointer gclass)
833 Directory *dir = (Directory *) object;
835 dir->known_items = g_hash_table_new(g_str_hash, g_str_equal);
836 dir->recheck_list = NULL;
837 dir->idle_callback = 0;
838 dir->scanning = FALSE;
839 dir->have_scanned = FALSE;
841 dir->users = NULL;
842 dir->needs_update = TRUE;
843 dir->notify_active = FALSE;
844 dir->pathname = NULL;
845 dir->error = NULL;
846 dir->rescan_timeout = -1;
847 #ifdef USE_DNOTIFY
848 dir->dnotify_fd = -1;
849 #endif
851 dir->new_items = g_ptr_array_new();
852 dir->up_items = g_ptr_array_new();
853 dir->gone_items = g_ptr_array_new();
856 static GType dir_get_type(void)
858 static GType type = 0;
860 if (!type)
862 static const GTypeInfo info =
864 sizeof (DirectoryClass),
865 NULL, /* base_init */
866 NULL, /* base_finalise */
867 directory_class_init,
868 NULL, /* class_finalise */
869 NULL, /* class_data */
870 sizeof(Directory),
871 0, /* n_preallocs */
872 directory_init
875 type = g_type_register_static(G_TYPE_OBJECT, "Directory",
876 &info, 0);
879 return type;
882 static Directory *dir_new(const char *pathname)
884 Directory *dir;
886 dir = g_object_new(dir_get_type(), NULL);
888 dir->pathname = g_strdup(pathname);
890 return dir;
893 /* Get the names of all files in the directory.
894 * Remove any DirItems that are no longer listed.
895 * Replace the recheck_list with the items found.
897 static void dir_rescan(Directory *dir)
899 GPtrArray *names;
900 DIR *d;
901 struct dirent *ent;
902 guint i;
903 const char *pathname;
904 int longest_len = -1;
905 GList *longest = NULL;
907 g_return_if_fail(dir != NULL);
909 pathname = dir->pathname;
911 dir->needs_update = FALSE;
913 names = g_ptr_array_new();
915 read_globicons();
916 mount_update(FALSE);
917 if (dir->error)
919 null_g_free(&dir->error);
920 dir_error_changed(dir);
923 /* Saves statting the parent for each item... */
924 if (mc_stat(pathname, &dir->stat_info))
926 dir->error = g_strdup_printf(_("Can't stat directory: %s"),
927 g_strerror(errno));
928 dir_error_changed(dir);
929 return; /* Report on attach */
932 d = mc_opendir(pathname);
933 if (!d)
935 dir->error = g_strdup_printf(_("Can't open directory: %s"),
936 g_strerror(errno));
937 dir_error_changed(dir);
938 return; /* Report on attach */
941 dir_set_scanning(dir, TRUE);
942 dir_merge_new(dir);
943 gdk_flush();
945 /* Make a list of all the names in the directory */
946 while ((ent = mc_readdir(d)))
948 if (ent->d_name[0] == '.')
950 if (ent->d_name[1] == '\0')
951 continue; /* Ignore '.' */
952 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
953 continue; /* Ignore '..' */
956 g_ptr_array_add(names, g_strdup(ent->d_name));
958 mc_closedir(d);
960 /* Sort, so the names are scanned in a sensible order */
961 qsort(names->pdata, names->len, sizeof(guchar *), sort_names);
963 /* Compare the list with the current DirItems, removing
964 * any that are missing.
966 remove_missing(dir, names);
968 free_recheck_list(dir);
970 /* For each name found, add it to the recheck_list.
971 * If the item is new, put a blank place-holder item in the directory.
973 for (i = 0; i < names->len; i++)
975 guchar *name = names->pdata[i];
976 int len = strlen(name);
978 dir->recheck_list = g_list_prepend(dir->recheck_list, name);
979 if (!g_hash_table_lookup(dir->known_items, name))
981 DirItem *new;
983 new = diritem_new(name);
984 g_ptr_array_add(dir->new_items, new);
987 if (name[0] != '.' && len > longest_len)
989 longest_len = len;
990 longest = dir->recheck_list;
994 dir_merge_new(dir);
996 dir->recheck_list = g_list_reverse(dir->recheck_list);
998 if (longest)
1000 gpointer data = longest->data;
1002 /* Move the longest item to the start. Helps with
1003 * auto sizing.
1005 dir->recheck_list = g_list_remove_link(dir->recheck_list,
1006 longest);
1007 dir->recheck_list = g_list_prepend(dir->recheck_list, data);
1010 g_ptr_array_free(names, TRUE);
1012 set_idle_callback(dir);
1013 dir_merge_new(dir);
1016 #ifdef USE_DNOTIFY
1017 /* Signal handler - don't do anything dangerous here */
1018 static void dnotify_handler(int sig, siginfo_t *si, void *data)
1020 /* Note: there is currently only one place to store the fd,
1021 * so we'll miss updates to several directories if they happen
1022 * close together.
1024 dnotify_last_fd = si->si_fd;
1025 dnotify_wakeup_flag = TRUE;
1026 write(to_wakeup_pipe, "\0", 1); /* Wake up! */
1028 #endif