4 * dir.c - Caches and updates directories
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
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
28 #include "gui_support.h"
34 GFSCache
*dir_cache
= NULL
;
36 /* Static prototypes */
37 static Directory
*load(char *pathname
, gpointer data
);
38 static void ref(Directory
*dir
, gpointer data
);
39 static void unref(Directory
*dir
, gpointer data
);
40 static int getref(Directory
*dir
, gpointer data
);
41 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
);
42 static void destroy(Directory
*dir
);
43 static void start_scanning(Directory
*dir
, char *pathname
);
44 static gint
idle_callback(Directory
*dir
);
45 static void init_for_scan(Directory
*dir
);
46 static void merge_new(Directory
*dir
);
49 /****************************************************************
50 * EXTERNAL INTERFACE *
51 ****************************************************************/
55 dir_cache
= g_fscache_new((GFSLoadFunc
) load
,
58 (GFSGetRefFunc
) getref
,
59 (GFSUpdateFunc
) update
, NULL
);
62 /* Periodically calls callback to notify about changes to the contents
64 * Before this function returns, it calls the callback once to add all
65 * the items currently in the directory (unless the dir is empty).
66 * If we are not scanning, it also calls update(DIR_END_SCAN).
68 void dir_attach(Directory
*dir
, DirCallback callback
, gpointer data
)
72 g_return_if_fail(dir
!= NULL
);
73 g_return_if_fail(callback
!= NULL
);
75 user
= g_new(DirUser
, 1);
76 user
->callback
= callback
;
79 dir
->users
= g_list_prepend(dir
->users
, user
);
84 callback(dir
, DIR_ADD
, dir
->items
, data
);
86 if (dir
->needs_update
)
87 start_scanning(dir
, dir
->pathname
);
90 delayed_error("ROX-Filer", dir
->error
);
93 callback(dir
, DIR_END_SCAN
, NULL
, data
);
96 /* Undo the effect of dir_attach */
97 void dir_detach(Directory
*dir
, DirCallback callback
, gpointer data
)
100 GList
*list
= dir
->users
;
102 g_return_if_fail(dir
!= NULL
);
103 g_return_if_fail(callback
!= NULL
);
107 user
= (DirUser
*) list
->data
;
108 if (user
->callback
== callback
&& user
->data
== data
)
111 dir
->users
= g_list_remove(dir
->users
, user
);
117 mc_closedir(dir
->dir_handle
);
118 dir
->dir_handle
= NULL
;
119 gtk_idle_remove(dir
->idle
);
121 dir
->needs_update
= TRUE
;
129 g_warning("dir_detach: Callback/data pair not attached!\n");
132 void dir_update(Directory
*dir
, gchar
*pathname
)
134 update(dir
, pathname
, NULL
);
137 int dir_item_cmp(const void *a
, const void *b
)
139 DirItem
*aa
= *((DirItem
**) a
);
140 DirItem
*bb
= *((DirItem
**) b
);
142 return strcmp(aa
->leafname
, bb
->leafname
);
145 void refresh_dirs(char *path
)
147 g_fscache_update(dir_cache
, path
);
150 /****************************************************************
151 * INTERNAL FUNCTIONS *
152 ****************************************************************/
154 static void free_items_array(GPtrArray
*array
)
158 for (i
= 0; i
< array
->len
; i
++)
160 DirItem
*item
= (DirItem
*) array
->pdata
[i
];
162 g_free(item
->leafname
);
166 g_ptr_array_free(array
, TRUE
);
169 /* Scanning has finished. Remove all the old items that have gone.
170 * Notify everyone who is watching us of the removed items and tell
171 * them that the scan is over.
173 static void sweep_deleted(Directory
*dir
)
175 GPtrArray
*array
= dir
->items
;
176 int items
= array
->len
;
179 DirItem
**from
= (DirItem
**) array
->pdata
;
180 DirItem
**to
= (DirItem
**) array
->pdata
;
181 GList
*list
= dir
->users
;
183 old
= g_ptr_array_new();
187 if (from
[0]->may_delete
)
189 g_ptr_array_add(old
, *from
);
201 g_ptr_array_set_size(array
, new_items
);
205 DirUser
*user
= (DirUser
*) list
->data
;
208 user
->callback(dir
, DIR_REMOVE
, old
, user
->data
);
209 user
->callback(dir
, DIR_END_SCAN
, NULL
, user
->data
);
214 free_items_array(old
);
218 /* Add all the new items to the items array.
219 * Notify everyone who is watching us.
221 static void merge_new(Directory
*dir
)
223 GList
*list
= dir
->users
;
224 GPtrArray
*new = dir
->new_items
;
225 GPtrArray
*up
= dir
->up_items
;
230 DirUser
*user
= (DirUser
*) list
->data
;
233 user
->callback(dir
, DIR_ADD
, new, user
->data
);
235 user
->callback(dir
, DIR_UPDATE
, up
, user
->data
);
241 for (i
= 0; i
< new->len
; i
++)
242 g_ptr_array_add(dir
->items
, new->pdata
[i
]);
243 qsort(dir
->items
->pdata
, dir
->items
->len
, sizeof(DirItem
*),
246 g_ptr_array_set_size(new, 0);
247 g_ptr_array_set_size(up
, 0);
250 static void init_for_scan(Directory
*dir
)
254 dir
->needs_update
= FALSE
;
255 dir
->done_some_scanning
= FALSE
;
257 for (i
= 0; i
< dir
->items
->len
; i
++)
258 ((DirItem
*) dir
->items
->pdata
[i
])->may_delete
= TRUE
;
261 static void start_scanning(Directory
*dir
, char *pathname
)
267 /* We are already scanning */
268 if (dir
->done_some_scanning
)
269 dir
->needs_update
= TRUE
;
281 dir
->dir_handle
= mc_opendir(pathname
);
283 if (!dir
->dir_handle
)
285 dir
->error
= g_strdup_printf("Can't open directory: %s",
287 return; /* Report on attach */
290 dir
->dir_start
= telldir(dir
->dir_handle
);
294 dir
->idle
= gtk_idle_add((GtkFunction
) idle_callback
, dir
);
296 /* Let all the filer windows know we're scanning */
297 for (next
= dir
->users
; next
; next
= next
->next
)
299 DirUser
*user
= (DirUser
*) next
->data
;
301 user
->callback(dir
, DIR_START_SCAN
, NULL
, user
->data
);
305 static gint
notify_timeout(gpointer data
)
307 Directory
*dir
= (Directory
*) data
;
309 g_return_val_if_fail(dir
->notify_active
== TRUE
, FALSE
);
313 dir
->notify_active
= FALSE
;
319 /* Call merge_new() after a while. */
320 static void delayed_notify(Directory
*dir
)
322 if (dir
->notify_active
)
325 gtk_timeout_add(500, notify_timeout
, dir
);
326 dir
->notify_active
= TRUE
;
329 static void insert_item(Directory
*dir
, struct dirent
*ent
)
331 static GString
*tmp
= NULL
;
334 GPtrArray
*array
= dir
->items
;
339 gboolean is_new
= FALSE
;
342 new.mime_type
= NULL
;
345 tmp
= make_path(dir
->pathname
, ent
->d_name
);
347 if (mc_lstat(tmp
->str
, &info
) == -1)
349 new.lstat_errno
= errno
;
350 new.base_type
= TYPE_ERROR
;
354 new.uid
= (uid_t
) -1;
355 new.gid
= (gid_t
) -1;
360 new.size
= info
.st_size
;
361 new.mode
= info
.st_mode
;
362 new.mtime
= info
.st_mtime
;
363 new.uid
= info
.st_uid
;
364 new.gid
= info
.st_gid
;
365 if (S_ISREG(info
.st_mode
))
366 new.base_type
= TYPE_FILE
;
367 else if (S_ISDIR(info
.st_mode
))
369 new.base_type
= TYPE_DIRECTORY
;
371 if (g_hash_table_lookup(mtab_mounts
, tmp
->str
))
372 new.flags
|= ITEM_FLAG_MOUNT_POINT
374 else if (g_hash_table_lookup(fstab_mounts
, tmp
->str
))
375 new.flags
|= ITEM_FLAG_MOUNT_POINT
;
377 else if (S_ISBLK(info
.st_mode
))
378 new.base_type
= TYPE_BLOCK_DEVICE
;
379 else if (S_ISCHR(info
.st_mode
))
380 new.base_type
= TYPE_CHAR_DEVICE
;
381 else if (S_ISFIFO(info
.st_mode
))
382 new.base_type
= TYPE_PIPE
;
383 else if (S_ISSOCK(info
.st_mode
))
384 new.base_type
= TYPE_SOCKET
;
385 else if (S_ISLNK(info
.st_mode
))
387 if (mc_stat(tmp
->str
, &info
))
388 new.base_type
= TYPE_ERROR
;
391 if (S_ISREG(info
.st_mode
))
392 new.base_type
= TYPE_FILE
;
393 else if (S_ISDIR(info
.st_mode
))
394 new.base_type
= TYPE_DIRECTORY
;
395 else if (S_ISBLK(info
.st_mode
))
396 new.base_type
= TYPE_BLOCK_DEVICE
;
397 else if (S_ISCHR(info
.st_mode
))
398 new.base_type
= TYPE_CHAR_DEVICE
;
399 else if (S_ISFIFO(info
.st_mode
))
400 new.base_type
= TYPE_PIPE
;
401 else if (S_ISSOCK(info
.st_mode
))
402 new.base_type
= TYPE_SOCKET
;
404 new.base_type
= TYPE_UNKNOWN
;
407 new.flags
|= ITEM_FLAG_SYMLINK
;
410 new.base_type
= TYPE_UNKNOWN
;
412 if (new.base_type
== TYPE_DIRECTORY
&&
413 !(new.flags
& ITEM_FLAG_MOUNT_POINT
))
415 uid_t uid
= info
.st_uid
;
417 /* Might be an application directory - better check...
418 * AppRun must have the same owner as the directory
419 * (to stop people putting an AppRun in, eg, /tmp)
421 g_string_append(tmp
, "/AppRun");
422 if (!mc_stat(tmp
->str
, &info
) && info
.st_uid
== uid
)
424 MaskedPixmap
*app_icon
;
426 new.flags
|= ITEM_FLAG_APPDIR
;
428 g_string_truncate(tmp
, tmp
->len
- 3);
429 g_string_append(tmp
, "Icon.xpm");
430 app_icon
= g_fscache_lookup(pixmap_cache
, tmp
->str
);
433 new.image
= app_icon
;
434 new.flags
|= ITEM_FLAG_TEMP_ICON
;
437 new.image
= default_pixmap
[TYPE_APPDIR
];
440 new.image
= default_pixmap
[TYPE_DIRECTORY
];
442 else if (new.base_type
== TYPE_FILE
)
444 /* Note: for symlinks we use need the mode of the target */
445 if (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
447 new.image
= default_pixmap
[TYPE_EXEC_FILE
];
448 new.flags
|= ITEM_FLAG_EXEC_FILE
;
452 new.mime_type
= type_from_path(tmp
->str
);
453 new.image
= type_to_icon(new.mime_type
);
454 new.flags
|= ITEM_FLAG_TEMP_ICON
;
458 new.image
= default_pixmap
[new.base_type
];
460 for (i
= 0; i
< array
->len
; i
++)
462 item
= (DirItem
*) array
->pdata
[i
];
464 if (strcmp(item
->leafname
, ent
->d_name
) == 0)
468 item
= g_new(DirItem
, 1);
469 item
->leafname
= g_strdup(ent
->d_name
);
470 g_ptr_array_add(dir
->new_items
, item
);
474 item
->may_delete
= FALSE
;
476 font
= gtk_widget_get_default_style()->font
;
477 new.name_width
= gdk_string_width(font
, item
->leafname
);
478 new.leafname
= item
->leafname
;
479 new.details_width
= gdk_string_width(fixed_font
, details(&new));
483 if (item
->flags
& ITEM_FLAG_TEMP_ICON
)
484 pixmap_unref(item
->image
);
485 if (item
->lstat_errno
== new.lstat_errno
486 && item
->base_type
== new.base_type
487 && item
->flags
== new.flags
488 && item
->size
== new.size
489 && item
->mode
== new.mode
490 && item
->mtime
== new.mtime
491 && item
->uid
== new.uid
492 && item
->gid
== new.gid
493 && item
->image
== new.image
494 && item
->mime_type
== new.mime_type
495 && item
->name_width
== new.name_width
496 && item
->details_width
== new.details_width
)
500 item
->image
= new.image
;
501 item
->lstat_errno
= new.lstat_errno
;
502 item
->base_type
= new.base_type
;
503 item
->flags
= new.flags
;
504 item
->size
= new.size
;
505 item
->mode
= new.mode
;
508 item
->mtime
= new.mtime
;
509 item
->mime_type
= new.mime_type
;
510 item
->name_width
= new.name_width
;
511 item
->details_width
= new.details_width
;
515 g_ptr_array_add(dir
->up_items
, item
);
520 static gint
idle_callback(Directory
*dir
)
524 dir
->done_some_scanning
= TRUE
;
528 ent
= mc_readdir(dir
->dir_handle
);
535 if (dir
->needs_update
)
537 mc_seekdir(dir
->dir_handle
, dir
->dir_start
);
542 mc_closedir(dir
->dir_handle
);
543 dir
->dir_handle
= NULL
;
545 return FALSE
; /* Stop */
548 insert_item(dir
, ent
);
550 } while (!gtk_events_pending());
555 static Directory
*load(char *pathname
, gpointer data
)
559 dir
= g_new(Directory
, 1);
561 dir
->items
= g_ptr_array_new();
562 dir
->new_items
= g_ptr_array_new();
563 dir
->up_items
= g_ptr_array_new();
565 dir
->dir_handle
= NULL
;
566 dir
->needs_update
= TRUE
;
567 dir
->notify_active
= FALSE
;
568 dir
->pathname
= g_strdup(pathname
);
574 static void destroy(Directory
*dir
)
576 g_return_if_fail(dir
->users
== NULL
);
578 g_print("[ destroy %p ]\n", dir
);
581 mc_closedir(dir
->dir_handle
);
582 gtk_idle_remove(dir
->idle
);
584 g_ptr_array_free(dir
->up_items
, TRUE
);
585 free_items_array(dir
->items
);
586 free_items_array(dir
->new_items
);
588 g_free(dir
->pathname
);
592 static void ref(Directory
*dir
, gpointer data
)
597 static void unref(Directory
*dir
, gpointer data
)
603 static int getref(Directory
*dir
, gpointer data
)
608 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
)
610 g_free(dir
->pathname
);
611 dir
->pathname
= g_strdup(pathname
);
613 start_scanning(dir
, pathname
);
616 delayed_error("ROX-Filer", dir
->error
);