4 * dir.c - Caches and updates directories
5 * Copyright (C) 1999, 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"
33 GFSCache
*dir_cache
= NULL
;
35 /* Static prototypes */
36 static Directory
*load(char *pathname
, gpointer data
);
37 static void ref(Directory
*dir
, gpointer data
);
38 static void unref(Directory
*dir
, gpointer data
);
39 static int getref(Directory
*dir
, gpointer data
);
40 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
);
41 static void destroy(Directory
*dir
);
42 static void start_scanning(Directory
*dir
, char *pathname
);
43 static gint
idle_callback(Directory
*dir
);
44 static void init_for_scan(Directory
*dir
);
45 static void merge_new(Directory
*dir
);
48 /****************************************************************
49 * EXTERNAL INTERFACE *
50 ****************************************************************/
54 dir_cache
= g_fscache_new((GFSLoadFunc
) load
,
57 (GFSGetRefFunc
) getref
,
58 (GFSUpdateFunc
) update
, NULL
);
61 /* Periodically calls callback to notify about changes to the contents
63 * Before this function returns, it calls the callback once to add all
64 * the items currently in the directory (unless the dir is empty).
65 * If we are not scanning, it also calls update(DIR_END_SCAN).
67 void dir_attach(Directory
*dir
, DirCallback callback
, gpointer data
)
71 g_return_if_fail(dir
!= NULL
);
72 g_return_if_fail(callback
!= NULL
);
74 user
= g_new(DirUser
, 1);
75 user
->callback
= callback
;
78 dir
->users
= g_list_prepend(dir
->users
, user
);
83 callback(dir
, DIR_ADD
, dir
->items
, data
);
85 if (dir
->needs_update
)
86 start_scanning(dir
, dir
->pathname
);
89 delayed_error("ROX-Filer", dir
->error
);
92 callback(dir
, DIR_END_SCAN
, NULL
, data
);
95 /* Undo the effect of dir_attach */
96 void dir_detach(Directory
*dir
, DirCallback callback
, gpointer data
)
99 GList
*list
= dir
->users
;
101 g_return_if_fail(dir
!= NULL
);
102 g_return_if_fail(callback
!= NULL
);
106 user
= (DirUser
*) list
->data
;
107 if (user
->callback
== callback
&& user
->data
== data
)
110 dir
->users
= g_list_remove(dir
->users
, user
);
116 closedir(dir
->dir_handle
);
117 dir
->dir_handle
= NULL
;
118 gtk_idle_remove(dir
->idle
);
120 dir
->needs_update
= TRUE
;
128 g_warning("dir_detach: Callback/data pair not attached!\n");
131 void dir_update(Directory
*dir
, gchar
*pathname
)
133 update(dir
, pathname
, NULL
);
136 int dir_item_cmp(const void *a
, const void *b
)
138 DirItem
*aa
= *((DirItem
**) a
);
139 DirItem
*bb
= *((DirItem
**) b
);
141 return strcmp(aa
->leafname
, bb
->leafname
);
144 void refresh_dirs(char *path
)
146 g_fscache_update(dir_cache
, path
);
149 /****************************************************************
150 * INTERNAL FUNCTIONS *
151 ****************************************************************/
153 static void free_items_array(GPtrArray
*array
)
157 for (i
= 0; i
< array
->len
; i
++)
159 DirItem
*item
= (DirItem
*) array
->pdata
[i
];
161 g_free(item
->leafname
);
165 g_ptr_array_free(array
, TRUE
);
168 /* Scanning has finished. Remove all the old items that have gone.
169 * Notify everyone who is watching us of the removed items and tell
170 * them that the scan is over.
172 static void sweep_deleted(Directory
*dir
)
174 GPtrArray
*array
= dir
->items
;
175 int items
= array
->len
;
178 DirItem
**from
= (DirItem
**) array
->pdata
;
179 DirItem
**to
= (DirItem
**) array
->pdata
;
180 GList
*list
= dir
->users
;
182 old
= g_ptr_array_new();
186 if (from
[0]->may_delete
)
188 g_ptr_array_add(old
, *from
);
200 g_ptr_array_set_size(array
, new_items
);
204 DirUser
*user
= (DirUser
*) list
->data
;
207 user
->callback(dir
, DIR_REMOVE
, old
, user
->data
);
208 user
->callback(dir
, DIR_END_SCAN
, NULL
, user
->data
);
213 free_items_array(old
);
217 /* Add all the new items to the items array.
218 * Notify everyone who is watching us.
220 static void merge_new(Directory
*dir
)
222 GList
*list
= dir
->users
;
223 GPtrArray
*new = dir
->new_items
;
224 GPtrArray
*up
= dir
->up_items
;
229 DirUser
*user
= (DirUser
*) list
->data
;
232 user
->callback(dir
, DIR_ADD
, new, user
->data
);
234 user
->callback(dir
, DIR_UPDATE
, up
, user
->data
);
240 for (i
= 0; i
< new->len
; i
++)
241 g_ptr_array_add(dir
->items
, new->pdata
[i
]);
242 qsort(dir
->items
->pdata
, dir
->items
->len
, sizeof(DirItem
*),
245 g_ptr_array_set_size(new, 0);
246 g_ptr_array_set_size(up
, 0);
249 static void init_for_scan(Directory
*dir
)
253 dir
->needs_update
= 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
)
263 dir
->needs_update
= TRUE
;
275 dir
->dir_handle
= opendir(pathname
);
277 if (!dir
->dir_handle
)
279 dir
->error
= g_strdup_printf("Can't open directory: %s",
281 return; /* Report on attach */
286 dir
->idle
= gtk_idle_add((GtkFunction
) idle_callback
, dir
);
289 static gint
notify_timeout(gpointer data
)
291 Directory
*dir
= (Directory
*) data
;
293 g_return_val_if_fail(dir
->notify_active
== TRUE
, FALSE
);
297 dir
->notify_active
= FALSE
;
303 /* Call merge_new() after a while. */
304 static void delayed_notify(Directory
*dir
)
306 if (dir
->notify_active
)
309 gtk_timeout_add(500, notify_timeout
, dir
);
310 dir
->notify_active
= TRUE
;
313 static void insert_item(Directory
*dir
, struct dirent
*ent
)
315 static GString
*tmp
= NULL
;
318 GPtrArray
*array
= dir
->items
;
323 gboolean is_new
= FALSE
;
326 new.mime_type
= NULL
;
332 tmp
= make_path(dir
->pathname
, ent
->d_name
);
334 if (lstat(tmp
->str
, &info
) == -1)
335 new.base_type
= TYPE_ERROR
;
338 new.size
= info
.st_size
;
339 new.mode
= info
.st_mode
;
340 new.mtime
= info
.st_mtime
;
341 if (S_ISREG(info
.st_mode
))
342 new.base_type
= TYPE_FILE
;
343 else if (S_ISDIR(info
.st_mode
))
345 new.base_type
= TYPE_DIRECTORY
;
347 if (g_hash_table_lookup(mtab_mounts
, tmp
->str
))
348 new.flags
|= ITEM_FLAG_MOUNT_POINT
350 else if (g_hash_table_lookup(fstab_mounts
, tmp
->str
))
351 new.flags
|= ITEM_FLAG_MOUNT_POINT
;
353 else if (S_ISBLK(info
.st_mode
))
354 new.base_type
= TYPE_BLOCK_DEVICE
;
355 else if (S_ISCHR(info
.st_mode
))
356 new.base_type
= TYPE_CHAR_DEVICE
;
357 else if (S_ISFIFO(info
.st_mode
))
358 new.base_type
= TYPE_PIPE
;
359 else if (S_ISSOCK(info
.st_mode
))
360 new.base_type
= TYPE_SOCKET
;
361 else if (S_ISLNK(info
.st_mode
))
363 if (stat(tmp
->str
, &info
))
364 new.base_type
= TYPE_ERROR
;
367 if (S_ISREG(info
.st_mode
))
368 new.base_type
= TYPE_FILE
;
369 else if (S_ISDIR(info
.st_mode
))
370 new.base_type
= TYPE_DIRECTORY
;
371 else if (S_ISBLK(info
.st_mode
))
372 new.base_type
= TYPE_BLOCK_DEVICE
;
373 else if (S_ISCHR(info
.st_mode
))
374 new.base_type
= TYPE_CHAR_DEVICE
;
375 else if (S_ISFIFO(info
.st_mode
))
376 new.base_type
= TYPE_PIPE
;
377 else if (S_ISSOCK(info
.st_mode
))
378 new.base_type
= TYPE_SOCKET
;
380 new.base_type
= TYPE_UNKNOWN
;
383 new.flags
|= ITEM_FLAG_SYMLINK
;
386 new.base_type
= TYPE_UNKNOWN
;
388 if (new.base_type
== TYPE_DIRECTORY
&&
389 !(new.flags
& ITEM_FLAG_MOUNT_POINT
))
391 uid_t uid
= info
.st_uid
;
393 /* Might be an application directory - better check...
394 * AppRun must have the same owner as the directory
395 * (to stop people putting an AppRun in, eg, /tmp)
397 g_string_append(tmp
, "/AppRun");
398 if (!stat(tmp
->str
, &info
) && info
.st_uid
== uid
)
400 MaskedPixmap
*app_icon
;
402 new.flags
|= ITEM_FLAG_APPDIR
;
404 g_string_truncate(tmp
, tmp
->len
- 3);
405 g_string_append(tmp
, "Icon.xpm");
406 app_icon
= g_fscache_lookup(pixmap_cache
, tmp
->str
);
409 new.image
= app_icon
;
410 new.flags
|= ITEM_FLAG_TEMP_ICON
;
413 new.image
= default_pixmap
+ TYPE_APPDIR
;
416 new.image
= default_pixmap
+ TYPE_DIRECTORY
;
418 else if (new.base_type
== TYPE_FILE
)
420 /* Note: for symlinks we use need the mode of the target */
421 if (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
423 new.image
= default_pixmap
+ TYPE_EXEC_FILE
;
424 new.flags
|= ITEM_FLAG_EXEC_FILE
;
428 new.mime_type
= type_from_path(tmp
->str
);
429 new.image
= type_to_icon(new.mime_type
);
430 new.flags
|= ITEM_FLAG_TEMP_ICON
;
434 new.image
= default_pixmap
+ new.base_type
;
436 for (i
= 0; i
< array
->len
; i
++)
438 item
= (DirItem
*) array
->pdata
[i
];
440 if (strcmp(item
->leafname
, ent
->d_name
) == 0)
444 item
= g_new(DirItem
, 1);
445 item
->leafname
= g_strdup(ent
->d_name
);
446 g_ptr_array_add(dir
->new_items
, item
);
450 item
->may_delete
= FALSE
;
452 font
= gtk_widget_get_default_style()->font
;
453 new.name_width
= gdk_string_width(font
, item
->leafname
);
454 new.leafname
= item
->leafname
;
455 new.details_width
= gdk_string_width(fixed_font
, details(&new));
458 && item
->base_type
== new.base_type
459 && item
->flags
== new.flags
460 && item
->size
== new.size
461 && item
->mode
== new.mode
462 && item
->mtime
== new.mtime
463 && item
->image
== new.image
464 && item
->mime_type
== new.mime_type
465 && item
->name_width
== new.name_width
466 && item
->details_width
== new.details_width
)
469 item
->base_type
= new.base_type
;
470 item
->flags
= new.flags
;
471 item
->size
= new.size
;
472 item
->mode
= new.mode
;
473 item
->mtime
= new.mtime
;
474 item
->image
= new.image
;
475 item
->mime_type
= new.mime_type
;
476 item
->name_width
= new.name_width
;
477 item
->details_width
= new.details_width
;
481 g_ptr_array_add(dir
->up_items
, item
);
486 static gint
idle_callback(Directory
*dir
)
492 ent
= readdir(dir
->dir_handle
);
499 if (dir
->needs_update
)
501 rewinddir(dir
->dir_handle
);
506 closedir(dir
->dir_handle
);
507 dir
->dir_handle
= NULL
;
509 return FALSE
; /* Stop */
512 insert_item(dir
, ent
);
514 } while (!gtk_events_pending());
519 static Directory
*load(char *pathname
, gpointer data
)
523 dir
= g_new(Directory
, 1);
525 dir
->items
= g_ptr_array_new();
526 dir
->new_items
= g_ptr_array_new();
527 dir
->up_items
= g_ptr_array_new();
529 dir
->dir_handle
= NULL
;
530 dir
->needs_update
= TRUE
;
531 dir
->notify_active
= FALSE
;
532 dir
->pathname
= g_strdup(pathname
);
538 static void destroy(Directory
*dir
)
540 g_return_if_fail(dir
->users
== NULL
);
542 g_print("[ destroy %p ]\n", dir
);
545 closedir(dir
->dir_handle
);
546 gtk_idle_remove(dir
->idle
);
548 g_ptr_array_free(dir
->up_items
, TRUE
);
549 free_items_array(dir
->items
);
550 free_items_array(dir
->new_items
);
552 g_free(dir
->pathname
);
556 static void ref(Directory
*dir
, gpointer data
)
561 static void unref(Directory
*dir
, gpointer data
)
567 static int getref(Directory
*dir
, gpointer data
)
572 static void update(Directory
*dir
, gchar
*pathname
, gpointer data
)
574 g_free(dir
->pathname
);
575 dir
->pathname
= g_strdup(pathname
);
577 start_scanning(dir
, pathname
);
580 delayed_error("ROX-Filer", dir
->error
);