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
26 #include "gui_support.h"
32 GFSCache
*dir_cache
= NULL
;
34 /* Static prototypes */
35 static Directory
*load(char *pathname
, gpointer data
);
36 static void ref(Directory
*dir
, gpointer data
);
37 static void unref(Directory
*dir
, gpointer data
);
38 static int getref(Directory
*dir
, gpointer data
);
39 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
);
40 static void destroy(Directory
*dir
);
41 static void start_scanning(Directory
*dir
, char *pathname
);
42 static gint
idle_callback(Directory
*dir
);
43 static void init_for_scan(Directory
*dir
);
44 static void merge_new(Directory
*dir
);
47 /****************************************************************
48 * EXTERNAL INTERFACE *
49 ****************************************************************/
53 dir_cache
= g_fscache_new((GFSLoadFunc
) load
,
56 (GFSGetRefFunc
) getref
,
57 (GFSUpdateFunc
) update
, NULL
);
60 /* Periodically calls callback to notify about changes to the contents
62 * Before this function returns, it calls the callback once to add all
63 * the items currently in the directory (unless the dir is empty).
64 * If we are not scanning, it also calls update(DIR_END_SCAN).
66 void dir_attach(Directory
*dir
, DirCallback callback
, gpointer data
)
70 g_return_if_fail(dir
!= NULL
);
71 g_return_if_fail(callback
!= NULL
);
73 user
= g_new(DirUser
, 1);
74 user
->callback
= callback
;
77 dir
->users
= g_list_prepend(dir
->users
, user
);
82 callback(dir
, DIR_ADD
, dir
->items
, data
);
84 if (dir
->needs_update
)
85 start_scanning(dir
, dir
->pathname
);
88 delayed_error("ROX-Filer", dir
->error
);
91 callback(dir
, DIR_END_SCAN
, NULL
, data
);
94 /* Undo the effect of dir_attach */
95 void dir_detach(Directory
*dir
, DirCallback callback
, gpointer data
)
98 GList
*list
= dir
->users
;
100 g_return_if_fail(dir
!= NULL
);
101 g_return_if_fail(callback
!= NULL
);
105 user
= (DirUser
*) list
->data
;
106 if (user
->callback
== callback
&& user
->data
== data
)
109 dir
->users
= g_list_remove(dir
->users
, user
);
115 closedir(dir
->dir_handle
);
116 dir
->dir_handle
= NULL
;
117 gtk_idle_remove(dir
->idle
);
119 dir
->needs_update
= TRUE
;
127 g_warning("dir_detach: Callback/data pair not attached!\n");
130 void dir_update(Directory
*dir
, gchar
*pathname
)
132 update(dir
, pathname
, NULL
);
135 int dir_item_cmp(const void *a
, const void *b
)
137 DirItem
*aa
= *((DirItem
**) a
);
138 DirItem
*bb
= *((DirItem
**) b
);
140 return strcmp(aa
->leafname
, bb
->leafname
);
143 void refresh_dirs(char *path
)
145 g_fscache_update(dir_cache
, path
);
148 /****************************************************************
149 * INTERNAL FUNCTIONS *
150 ****************************************************************/
152 static void free_items_array(GPtrArray
*array
)
156 for (i
= 0; i
< array
->len
; i
++)
158 DirItem
*item
= (DirItem
*) array
->pdata
[i
];
160 g_free(item
->leafname
);
164 g_ptr_array_free(array
, TRUE
);
167 /* Scanning has finished. Remove all the old items that have gone.
168 * Notify everyone who is watching us of the removed items and tell
169 * them that the scan is over.
171 static void sweep_deleted(Directory
*dir
)
173 GPtrArray
*array
= dir
->items
;
174 int items
= array
->len
;
177 DirItem
**from
= (DirItem
**) array
->pdata
;
178 DirItem
**to
= (DirItem
**) array
->pdata
;
179 GList
*list
= dir
->users
;
181 old
= g_ptr_array_new();
185 if (from
[0]->may_delete
)
187 g_ptr_array_add(old
, *from
);
199 g_ptr_array_set_size(array
, new_items
);
203 DirUser
*user
= (DirUser
*) list
->data
;
206 user
->callback(dir
, DIR_REMOVE
, old
, user
->data
);
207 user
->callback(dir
, DIR_END_SCAN
, NULL
, user
->data
);
212 free_items_array(old
);
216 /* Add all the new items to the items array.
217 * Notify everyone who is watching us.
219 static void merge_new(Directory
*dir
)
221 GList
*list
= dir
->users
;
222 GPtrArray
*new = dir
->new_items
;
223 GPtrArray
*up
= dir
->up_items
;
228 DirUser
*user
= (DirUser
*) list
->data
;
231 user
->callback(dir
, DIR_ADD
, new, user
->data
);
233 user
->callback(dir
, DIR_UPDATE
, up
, user
->data
);
239 for (i
= 0; i
< new->len
; i
++)
240 g_ptr_array_add(dir
->items
, new->pdata
[i
]);
241 qsort(dir
->items
->pdata
, dir
->items
->len
, sizeof(DirItem
*),
244 g_ptr_array_set_size(new, 0);
245 g_ptr_array_set_size(up
, 0);
248 static void init_for_scan(Directory
*dir
)
252 dir
->needs_update
= FALSE
;
253 dir
->done_some_scanning
= FALSE
;
255 for (i
= 0; i
< dir
->items
->len
; i
++)
256 ((DirItem
*) dir
->items
->pdata
[i
])->may_delete
= TRUE
;
259 static void start_scanning(Directory
*dir
, char *pathname
)
265 /* We are already scanning */
266 if (dir
->done_some_scanning
)
267 dir
->needs_update
= TRUE
;
279 dir
->dir_handle
= opendir(pathname
);
281 if (!dir
->dir_handle
)
283 dir
->error
= g_strdup_printf("Can't open directory: %s",
285 return; /* Report on attach */
290 dir
->idle
= gtk_idle_add((GtkFunction
) idle_callback
, dir
);
292 /* Let all the filer windows know we're scanning */
293 for (next
= dir
->users
; next
; next
= next
->next
)
295 DirUser
*user
= (DirUser
*) next
->data
;
297 user
->callback(dir
, DIR_START_SCAN
, NULL
, user
->data
);
301 static gint
notify_timeout(gpointer data
)
303 Directory
*dir
= (Directory
*) data
;
305 g_return_val_if_fail(dir
->notify_active
== TRUE
, FALSE
);
309 dir
->notify_active
= FALSE
;
315 /* Call merge_new() after a while. */
316 static void delayed_notify(Directory
*dir
)
318 if (dir
->notify_active
)
321 gtk_timeout_add(500, notify_timeout
, dir
);
322 dir
->notify_active
= TRUE
;
325 static void insert_item(Directory
*dir
, struct dirent
*ent
)
327 static GString
*tmp
= NULL
;
330 GPtrArray
*array
= dir
->items
;
335 gboolean is_new
= FALSE
;
338 new.mime_type
= NULL
;
344 tmp
= make_path(dir
->pathname
, ent
->d_name
);
346 if (lstat(tmp
->str
, &info
) == -1)
347 new.base_type
= TYPE_ERROR
;
350 new.size
= info
.st_size
;
351 new.mode
= info
.st_mode
;
352 new.mtime
= info
.st_mtime
;
353 new.uid
= info
.st_uid
;
354 new.gid
= info
.st_gid
;
355 if (S_ISREG(info
.st_mode
))
356 new.base_type
= TYPE_FILE
;
357 else if (S_ISDIR(info
.st_mode
))
359 new.base_type
= TYPE_DIRECTORY
;
361 if (g_hash_table_lookup(mtab_mounts
, tmp
->str
))
362 new.flags
|= ITEM_FLAG_MOUNT_POINT
364 else if (g_hash_table_lookup(fstab_mounts
, tmp
->str
))
365 new.flags
|= ITEM_FLAG_MOUNT_POINT
;
367 else if (S_ISBLK(info
.st_mode
))
368 new.base_type
= TYPE_BLOCK_DEVICE
;
369 else if (S_ISCHR(info
.st_mode
))
370 new.base_type
= TYPE_CHAR_DEVICE
;
371 else if (S_ISFIFO(info
.st_mode
))
372 new.base_type
= TYPE_PIPE
;
373 else if (S_ISSOCK(info
.st_mode
))
374 new.base_type
= TYPE_SOCKET
;
375 else if (S_ISLNK(info
.st_mode
))
377 if (stat(tmp
->str
, &info
))
378 new.base_type
= TYPE_ERROR
;
381 if (S_ISREG(info
.st_mode
))
382 new.base_type
= TYPE_FILE
;
383 else if (S_ISDIR(info
.st_mode
))
384 new.base_type
= TYPE_DIRECTORY
;
385 else if (S_ISBLK(info
.st_mode
))
386 new.base_type
= TYPE_BLOCK_DEVICE
;
387 else if (S_ISCHR(info
.st_mode
))
388 new.base_type
= TYPE_CHAR_DEVICE
;
389 else if (S_ISFIFO(info
.st_mode
))
390 new.base_type
= TYPE_PIPE
;
391 else if (S_ISSOCK(info
.st_mode
))
392 new.base_type
= TYPE_SOCKET
;
394 new.base_type
= TYPE_UNKNOWN
;
397 new.flags
|= ITEM_FLAG_SYMLINK
;
400 new.base_type
= TYPE_UNKNOWN
;
402 if (new.base_type
== TYPE_DIRECTORY
&&
403 !(new.flags
& ITEM_FLAG_MOUNT_POINT
))
405 uid_t uid
= info
.st_uid
;
407 /* Might be an application directory - better check...
408 * AppRun must have the same owner as the directory
409 * (to stop people putting an AppRun in, eg, /tmp)
411 g_string_append(tmp
, "/AppRun");
412 if (!stat(tmp
->str
, &info
) && info
.st_uid
== uid
)
414 MaskedPixmap
*app_icon
;
416 new.flags
|= ITEM_FLAG_APPDIR
;
418 g_string_truncate(tmp
, tmp
->len
- 3);
419 g_string_append(tmp
, "Icon.xpm");
420 app_icon
= g_fscache_lookup(pixmap_cache
, tmp
->str
);
423 new.image
= app_icon
;
424 new.flags
|= ITEM_FLAG_TEMP_ICON
;
427 new.image
= default_pixmap
+ TYPE_APPDIR
;
430 new.image
= default_pixmap
+ TYPE_DIRECTORY
;
432 else if (new.base_type
== TYPE_FILE
)
434 /* Note: for symlinks we use need the mode of the target */
435 if (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
437 new.image
= default_pixmap
+ TYPE_EXEC_FILE
;
438 new.flags
|= ITEM_FLAG_EXEC_FILE
;
442 new.mime_type
= type_from_path(tmp
->str
);
443 new.image
= type_to_icon(new.mime_type
);
444 new.flags
|= ITEM_FLAG_TEMP_ICON
;
448 new.image
= default_pixmap
+ new.base_type
;
450 for (i
= 0; i
< array
->len
; i
++)
452 item
= (DirItem
*) array
->pdata
[i
];
454 if (strcmp(item
->leafname
, ent
->d_name
) == 0)
458 item
= g_new(DirItem
, 1);
459 item
->leafname
= g_strdup(ent
->d_name
);
460 g_ptr_array_add(dir
->new_items
, item
);
464 item
->may_delete
= FALSE
;
466 font
= gtk_widget_get_default_style()->font
;
467 new.name_width
= gdk_string_width(font
, item
->leafname
);
468 new.leafname
= item
->leafname
;
469 new.details_width
= gdk_string_width(fixed_font
, details(&new));
472 && item
->base_type
== new.base_type
473 && item
->flags
== new.flags
474 && item
->size
== new.size
475 && item
->mode
== new.mode
476 && item
->mtime
== new.mtime
477 && item
->uid
== new.uid
478 && item
->gid
== new.gid
479 && item
->image
== new.image
480 && item
->mime_type
== new.mime_type
481 && item
->name_width
== new.name_width
482 && item
->details_width
== new.details_width
)
485 item
->base_type
= new.base_type
;
486 item
->flags
= new.flags
;
487 item
->size
= new.size
;
488 item
->mode
= new.mode
;
491 item
->mtime
= new.mtime
;
492 item
->image
= new.image
;
493 item
->mime_type
= new.mime_type
;
494 item
->name_width
= new.name_width
;
495 item
->details_width
= new.details_width
;
499 g_ptr_array_add(dir
->up_items
, item
);
504 static gint
idle_callback(Directory
*dir
)
508 dir
->done_some_scanning
= TRUE
;
512 ent
= readdir(dir
->dir_handle
);
519 if (dir
->needs_update
)
521 rewinddir(dir
->dir_handle
);
526 closedir(dir
->dir_handle
);
527 dir
->dir_handle
= NULL
;
529 return FALSE
; /* Stop */
532 insert_item(dir
, ent
);
534 } while (!gtk_events_pending());
539 static Directory
*load(char *pathname
, gpointer data
)
543 dir
= g_new(Directory
, 1);
545 dir
->items
= g_ptr_array_new();
546 dir
->new_items
= g_ptr_array_new();
547 dir
->up_items
= g_ptr_array_new();
549 dir
->dir_handle
= NULL
;
550 dir
->needs_update
= TRUE
;
551 dir
->notify_active
= FALSE
;
552 dir
->pathname
= g_strdup(pathname
);
558 static void destroy(Directory
*dir
)
560 g_return_if_fail(dir
->users
== NULL
);
562 g_print("[ destroy %p ]\n", dir
);
565 closedir(dir
->dir_handle
);
566 gtk_idle_remove(dir
->idle
);
568 g_ptr_array_free(dir
->up_items
, TRUE
);
569 free_items_array(dir
->items
);
570 free_items_array(dir
->new_items
);
572 g_free(dir
->pathname
);
576 static void ref(Directory
*dir
, gpointer data
)
581 static void unref(Directory
*dir
, gpointer data
)
587 static int getref(Directory
*dir
, gpointer data
)
592 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
)
594 g_free(dir
->pathname
);
595 dir
->pathname
= g_strdup(pathname
);
597 start_scanning(dir
, pathname
);
600 delayed_error("ROX-Filer", dir
->error
);