4 * dir.c - Caches and updates directories
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "gui_support.h"
39 GFSCache
*dir_cache
= NULL
;
41 /* Static prototypes */
42 static Directory
*load(char *pathname
, gpointer data
);
43 static void ref(Directory
*dir
, gpointer data
);
44 static void unref(Directory
*dir
, gpointer data
);
45 static int getref(Directory
*dir
, gpointer data
);
46 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
);
47 static void destroy(Directory
*dir
);
48 static void start_scanning(Directory
*dir
, char *pathname
);
49 static gint
idle_callback(Directory
*dir
);
50 static void init_for_scan(Directory
*dir
);
51 static void merge_new(Directory
*dir
);
54 /****************************************************************
55 * EXTERNAL INTERFACE *
56 ****************************************************************/
60 dir_cache
= g_fscache_new((GFSLoadFunc
) load
,
63 (GFSGetRefFunc
) getref
,
64 (GFSUpdateFunc
) update
, NULL
);
67 /* Periodically calls callback to notify about changes to the contents
69 * Before this function returns, it calls the callback once to add all
70 * the items currently in the directory (unless the dir is empty).
71 * If we are not scanning, it also calls update(DIR_END_SCAN).
73 void dir_attach(Directory
*dir
, DirCallback callback
, gpointer data
)
77 g_return_if_fail(dir
!= NULL
);
78 g_return_if_fail(callback
!= NULL
);
80 user
= g_new(DirUser
, 1);
81 user
->callback
= callback
;
84 dir
->users
= g_list_prepend(dir
->users
, user
);
89 callback(dir
, DIR_ADD
, dir
->items
, data
);
91 if (dir
->needs_update
)
92 start_scanning(dir
, dir
->pathname
);
95 delayed_error(PROJECT
, dir
->error
);
98 callback(dir
, DIR_END_SCAN
, NULL
, data
);
101 /* Undo the effect of dir_attach */
102 void dir_detach(Directory
*dir
, DirCallback callback
, gpointer data
)
105 GList
*list
= dir
->users
;
107 g_return_if_fail(dir
!= NULL
);
108 g_return_if_fail(callback
!= NULL
);
112 user
= (DirUser
*) list
->data
;
113 if (user
->callback
== callback
&& user
->data
== data
)
116 dir
->users
= g_list_remove(dir
->users
, user
);
122 mc_closedir(dir
->dir_handle
);
123 dir
->dir_handle
= NULL
;
124 gtk_idle_remove(dir
->idle
);
126 dir
->needs_update
= TRUE
;
134 g_warning("dir_detach: Callback/data pair not attached!\n");
137 void dir_update(Directory
*dir
, gchar
*pathname
)
139 update(dir
, pathname
, NULL
);
142 int dir_item_cmp(const void *a
, const void *b
)
144 DirItem
*aa
= *((DirItem
**) a
);
145 DirItem
*bb
= *((DirItem
**) b
);
147 return strcmp(aa
->leafname
, bb
->leafname
);
150 void refresh_dirs(char *path
)
152 g_fscache_update(dir_cache
, path
);
155 /* Bring this item's structure uptodate */
156 void dir_restat(guchar
*path
, DirItem
*item
)
160 if (item
->image
&& item
->flags
& ITEM_FLAG_TEMP_ICON
)
162 pixmap_unref(item
->image
);
166 item
->mime_type
= NULL
;
168 if (mc_lstat(path
, &info
) == -1)
170 item
->lstat_errno
= errno
;
171 item
->base_type
= TYPE_ERROR
;
175 item
->uid
= (uid_t
) -1;
176 item
->gid
= (gid_t
) -1;
180 item
->lstat_errno
= 0;
181 item
->size
= info
.st_size
;
182 item
->mode
= info
.st_mode
;
183 item
->mtime
= info
.st_mtime
;
184 item
->uid
= info
.st_uid
;
185 item
->gid
= info
.st_gid
;
187 if (S_ISLNK(info
.st_mode
))
189 if (mc_stat(path
, &info
))
190 item
->base_type
= TYPE_ERROR
;
193 mode_to_base_type(info
.st_mode
);
195 item
->flags
|= ITEM_FLAG_SYMLINK
;
199 item
->base_type
= mode_to_base_type(info
.st_mode
);
201 if (item
->base_type
== TYPE_DIRECTORY
)
203 if (g_hash_table_lookup(mtab_mounts
, path
))
204 item
->flags
|= ITEM_FLAG_MOUNT_POINT
206 else if (g_hash_table_lookup(fstab_mounts
,
208 item
->flags
|= ITEM_FLAG_MOUNT_POINT
;
213 if (item
->base_type
== TYPE_DIRECTORY
&&
214 !(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
216 uid_t uid
= info
.st_uid
;
220 /* Might be an application directory - better check...
221 * AppRun must have the same owner as the directory
222 * (to stop people putting an AppRun in, eg, /tmp)
224 path_len
= strlen(path
);
225 /* (sizeof(string) includes \0) */
226 tmp
= g_malloc(path_len
+ sizeof("/AppIcon.xpm"));
227 sprintf(tmp
, "%s/%s", path
, "AppRun");
228 if (!mc_stat(tmp
, &info
) && info
.st_uid
== uid
)
230 MaskedPixmap
*app_icon
;
232 item
->flags
|= ITEM_FLAG_APPDIR
;
234 strcpy(tmp
+ path_len
+ 4, "Icon.xpm");
235 app_icon
= g_fscache_lookup(pixmap_cache
, tmp
);
238 item
->image
= app_icon
;
239 item
->flags
|= ITEM_FLAG_TEMP_ICON
;
242 item
->image
= im_appdir
;
246 else if (item
->base_type
== TYPE_FILE
)
248 /* Note: for symlinks we use need the mode of the target */
249 if (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
251 item
->image
= im_exec_file
;
252 item
->flags
|= ITEM_FLAG_EXEC_FILE
;
256 item
->mime_type
= type_from_path(path
);
257 item
->flags
|= ITEM_FLAG_TEMP_ICON
;
261 if (!item
->mime_type
)
262 item
->mime_type
= mime_type_from_base_type(item
->base_type
);
265 item
->image
= type_to_icon(item
->mime_type
);
268 /* Fill in the item structure with the appropriate details.
269 * 'leafname' field is set to NULL; text_width is unset.
271 void dir_stat(guchar
*path
, DirItem
*item
)
273 item
->leafname
= NULL
;
274 item
->may_delete
= FALSE
;
277 dir_restat(path
, item
);
280 /* Frees all fields in the icon, but does not free the icon structure
281 * itself (because it might be part of a larger structure).
283 void dir_item_clear(DirItem
*item
)
285 g_return_if_fail(item
!= NULL
);
287 if (item
->flags
& ITEM_FLAG_TEMP_ICON
)
288 pixmap_unref(item
->image
);
289 g_free(item
->leafname
);
292 /* When something has happened to a particular object, call this
293 * and all appropriate changes will be made.
294 * Currently, we just extract the dirname and rescan...
296 void dir_check_this(guchar
*path
)
301 real_path
= pathdup(path
);
303 slash
= strrchr(real_path
, '/');
308 refresh_dirs(real_path
);
314 /****************************************************************
315 * INTERNAL FUNCTIONS *
316 ****************************************************************/
318 static void free_items_array(GPtrArray
*array
)
322 for (i
= 0; i
< array
->len
; i
++)
324 DirItem
*item
= (DirItem
*) array
->pdata
[i
];
326 dir_item_clear(item
);
330 g_ptr_array_free(array
, TRUE
);
333 /* Scanning has finished. Remove all the old items that have gone.
334 * Notify everyone who is watching us of the removed items and tell
335 * them that the scan is over, unless 'needs_update'.
337 static void sweep_deleted(Directory
*dir
)
339 GPtrArray
*array
= dir
->items
;
340 int items
= array
->len
;
343 DirItem
**from
= (DirItem
**) array
->pdata
;
344 DirItem
**to
= (DirItem
**) array
->pdata
;
345 GList
*list
= dir
->users
;
347 old
= g_ptr_array_new();
351 if (from
[0]->may_delete
)
353 g_ptr_array_add(old
, *from
);
365 g_ptr_array_set_size(array
, new_items
);
369 DirUser
*user
= (DirUser
*) list
->data
;
372 user
->callback(dir
, DIR_REMOVE
, old
, user
->data
);
373 if (!dir
->needs_update
)
374 user
->callback(dir
, DIR_END_SCAN
, NULL
, user
->data
);
379 free_items_array(old
);
383 /* Add all the new items to the items array.
384 * Notify everyone who is watching us.
386 static void merge_new(Directory
*dir
)
388 GList
*list
= dir
->users
;
389 GPtrArray
*new = dir
->new_items
;
390 GPtrArray
*up
= dir
->up_items
;
395 DirUser
*user
= (DirUser
*) list
->data
;
398 user
->callback(dir
, DIR_ADD
, new, user
->data
);
400 user
->callback(dir
, DIR_UPDATE
, up
, user
->data
);
406 for (i
= 0; i
< new->len
; i
++)
407 g_ptr_array_add(dir
->items
, new->pdata
[i
]);
408 qsort(dir
->items
->pdata
, dir
->items
->len
, sizeof(DirItem
*),
411 g_ptr_array_set_size(new, 0);
412 g_ptr_array_set_size(up
, 0);
415 static void init_for_scan(Directory
*dir
)
419 dir
->needs_update
= FALSE
;
420 dir
->done_some_scanning
= FALSE
;
422 for (i
= 0; i
< dir
->items
->len
; i
++)
423 ((DirItem
*) dir
->items
->pdata
[i
])->may_delete
= TRUE
;
426 static void start_scanning(Directory
*dir
, char *pathname
)
432 /* We are already scanning */
433 if (dir
->done_some_scanning
)
434 dir
->needs_update
= TRUE
;
446 dir
->dir_handle
= mc_opendir(pathname
);
448 if (!dir
->dir_handle
)
450 dir
->error
= g_strdup_printf(_("Can't open directory: %s"),
452 return; /* Report on attach */
455 dir
->dir_start
= mc_telldir(dir
->dir_handle
);
459 dir
->idle
= gtk_idle_add((GtkFunction
) idle_callback
, dir
);
461 /* Let all the filer windows know we're scanning */
462 for (next
= dir
->users
; next
; next
= next
->next
)
464 DirUser
*user
= (DirUser
*) next
->data
;
466 user
->callback(dir
, DIR_START_SCAN
, NULL
, user
->data
);
470 static gint
notify_timeout(gpointer data
)
472 Directory
*dir
= (Directory
*) data
;
474 g_return_val_if_fail(dir
->notify_active
== TRUE
, FALSE
);
478 dir
->notify_active
= FALSE
;
484 /* Call merge_new() after a while. */
485 static void delayed_notify(Directory
*dir
)
487 if (dir
->notify_active
)
490 gtk_timeout_add(500, notify_timeout
, dir
);
491 dir
->notify_active
= TRUE
;
494 static void insert_item(Directory
*dir
, struct dirent
*ent
)
496 static GString
*tmp
= NULL
;
499 GPtrArray
*array
= dir
->items
;
503 gboolean is_new
= FALSE
;
505 tmp
= make_path(dir
->pathname
, ent
->d_name
);
506 dir_stat(tmp
->str
, &new);
508 /* Is an item with this name already listed? */
509 for (i
= 0; i
< array
->len
; i
++)
511 item
= (DirItem
*) array
->pdata
[i
];
513 if (strcmp(item
->leafname
, ent
->d_name
) == 0)
517 item
= g_new(DirItem
, 1);
518 item
->leafname
= g_strdup(ent
->d_name
);
519 g_ptr_array_add(dir
->new_items
, item
);
523 item
->may_delete
= FALSE
;
525 font
= gtk_widget_get_default_style()->font
;
526 new.name_width
= gdk_string_width(font
, item
->leafname
);
527 new.leafname
= item
->leafname
;
531 if (item
->flags
& ITEM_FLAG_TEMP_ICON
)
532 pixmap_unref(item
->image
);
533 if (item
->lstat_errno
== new.lstat_errno
534 && item
->base_type
== new.base_type
535 && item
->flags
== new.flags
536 && item
->size
== new.size
537 && item
->mode
== new.mode
538 && item
->mtime
== new.mtime
539 && item
->uid
== new.uid
540 && item
->gid
== new.gid
541 && item
->image
== new.image
542 && item
->mime_type
== new.mime_type
543 && item
->name_width
== new.name_width
)
547 item
->image
= new.image
;
548 item
->lstat_errno
= new.lstat_errno
;
549 item
->base_type
= new.base_type
;
550 item
->flags
= new.flags
;
551 item
->size
= new.size
;
552 item
->mode
= new.mode
;
555 item
->mtime
= new.mtime
;
556 item
->mime_type
= new.mime_type
;
557 item
->name_width
= new.name_width
;
561 g_ptr_array_add(dir
->up_items
, item
);
566 static gint
idle_callback(Directory
*dir
)
570 dir
->done_some_scanning
= TRUE
;
574 ent
= mc_readdir(dir
->dir_handle
);
581 if (dir
->needs_update
)
583 mc_seekdir(dir
->dir_handle
, dir
->dir_start
);
588 mc_closedir(dir
->dir_handle
);
589 dir
->dir_handle
= NULL
;
591 return FALSE
; /* Stop */
594 insert_item(dir
, ent
);
596 } while (!gtk_events_pending());
601 static Directory
*load(char *pathname
, gpointer data
)
605 dir
= g_new(Directory
, 1);
607 dir
->items
= g_ptr_array_new();
608 dir
->new_items
= g_ptr_array_new();
609 dir
->up_items
= g_ptr_array_new();
611 dir
->dir_handle
= NULL
;
612 dir
->needs_update
= TRUE
;
613 dir
->notify_active
= FALSE
;
614 dir
->pathname
= g_strdup(pathname
);
620 static void destroy(Directory
*dir
)
622 g_return_if_fail(dir
->users
== NULL
);
624 g_print("[ destroy %p ]\n", dir
);
627 mc_closedir(dir
->dir_handle
);
628 gtk_idle_remove(dir
->idle
);
630 g_ptr_array_free(dir
->up_items
, TRUE
);
631 free_items_array(dir
->items
);
632 free_items_array(dir
->new_items
);
634 g_free(dir
->pathname
);
638 static void ref(Directory
*dir
, gpointer data
)
643 static void unref(Directory
*dir
, gpointer data
)
649 static int getref(Directory
*dir
, gpointer data
)
654 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
)
656 g_free(dir
->pathname
);
657 dir
->pathname
= g_strdup(pathname
);
659 start_scanning(dir
, pathname
);
662 delayed_error(PROJECT
, dir
->error
);