aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjylefort <jylefort@FreeBSD.org>2005-06-11 01:38:28 +0800
committerjylefort <jylefort@FreeBSD.org>2005-06-11 01:38:28 +0800
commitcf8305cbff7e26bb1d644758131d09530af7ca26 (patch)
tree60f1ee7d228eb23623d4042fb78b2b2699910f49
parent06232bb20be97667a6c0f557ce2a46cca35eb53a (diff)
downloadfreebsd-ports-gnome-cf8305cbff7e26bb1d644758131d09530af7ca26.tar.gz
freebsd-ports-gnome-cf8305cbff7e26bb1d644758131d09530af7ca26.tar.zst
freebsd-ports-gnome-cf8305cbff7e26bb1d644758131d09530af7ca26.zip
Update to 0.1.1
-rw-r--r--devel/gamin/Makefile2
-rw-r--r--devel/gamin/distinfo4
-rw-r--r--devel/gamin/files/patch-server_gam_kqueue.c1687
-rw-r--r--devel/gamin/files/patch-server_gam_kqueue.h19
4 files changed, 3 insertions, 1709 deletions
diff --git a/devel/gamin/Makefile b/devel/gamin/Makefile
index 1f6b956cf8e5..275908cf7054 100644
--- a/devel/gamin/Makefile
+++ b/devel/gamin/Makefile
@@ -6,7 +6,7 @@
#
PORTNAME= gamin
-PORTVERSION= 0.1.0
+PORTVERSION= 0.1.1
CATEGORIES?= devel
MASTER_SITES= http://www.gnome.org/~veillard/gamin/sources/
diff --git a/devel/gamin/distinfo b/devel/gamin/distinfo
index 7c3ea60750c5..33c432b80685 100644
--- a/devel/gamin/distinfo
+++ b/devel/gamin/distinfo
@@ -1,2 +1,2 @@
-MD5 (gamin-0.1.0.tar.gz) = 282f34278af3367150e163136382d22d
-SIZE (gamin-0.1.0.tar.gz) = 498055
+MD5 (gamin-0.1.1.tar.gz) = 5c5330c3df26ece7d71b69a0b8f4a126
+SIZE (gamin-0.1.1.tar.gz) = 512316
diff --git a/devel/gamin/files/patch-server_gam_kqueue.c b/devel/gamin/files/patch-server_gam_kqueue.c
deleted file mode 100644
index 6ea99a963283..000000000000
--- a/devel/gamin/files/patch-server_gam_kqueue.c
+++ /dev/null
@@ -1,1687 +0,0 @@
---- server/gam_kqueue.c.orig Thu May 19 20:34:55 2005
-+++ server/gam_kqueue.c Thu May 19 20:35:11 2005
-@@ -1,5 +1,37 @@
- /*
-+ * gam_kqueue.c - a kqueue(2) Gamin backend
-+ *
-+ * Notes:
-+ *
-+ * * http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=0650&db=bks&fname=/SGI_Developer/books/IIDsktp_IG/sgi_html/ch08.html
-+ * states that FAM does not follow monitored symbolic links: we
-+ * do the same (note that regarding
-+ * http://oss.sgi.com/bugzilla/show_bug.cgi?id=405, we do what
-+ * FAM should do: we do not call g_dir_open() if the file is a
-+ * symbolic link).
-+ *
-+ * * kqueue cannot monitor files residing on anything but a UFS
-+ * file system. If kqueue cannot monitor a file, this backend
-+ * will poll it periodically.
-+ *
-+ * * Monitoring a file with kqueue prevents the file system it
-+ * resides on from being unmounted, because kqueue can only
-+ * monitor open files.
-+ *
-+ * * The creation of missing monitored files is detected by
-+ * performing a lstat() every second. Although using kqueue to
-+ * monitor the parent directory is technically feasible, it
-+ * would introduce a lot of complexity in the code.
-+ *
-+ * * In light of the previous points, it appears that:
-+ *
-+ * - kqueue needs to be augmented with a filename-based
-+ * monitoring facility;
-+ *
-+ * - kqueue needs to be moved out the UFS code.
-+ *
- * Copyright (C) 2005 Joe Marcus Clarke <marcus@FreeBSD.org>
-+ * Copyright (C) 2005 Jean-Yves Lefort <jylefort@FreeBSD.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
-@@ -16,508 +48,1068 @@
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
--
--#include "server_config.h"
-+#include "config.h"
-+#include <string.h>
-+#include <fcntl.h>
-+#include <unistd.h>
- #include <sys/types.h>
-+#include <sys/sysctl.h>
-+#include <sys/stat.h>
- #include <sys/event.h>
- #include <sys/time.h>
--#include <fcntl.h>
--#include <sys/ioctl.h>
--#include <signal.h>
--#include <unistd.h>
--#include <stdio.h>
--#include <string.h>
--#include <glib.h>
-+#include <errno.h>
- #include "gam_error.h"
- #include "gam_kqueue.h"
--#include "gam_tree.h"
- #include "gam_event.h"
- #include "gam_server.h"
--#include "gam_event.h"
-
--typedef struct {
-- char *path;
-- int fd;
-- int refcount;
-- gboolean isdir;
-- GList *subs;
-- GSList *dirlist;
--} KQueueData;
-+/*** tunable constants, modify to tweak the backend aggressivity *************/
-
--static GHashTable *dir_path_hash = NULL;
--static GHashTable *file_path_hash = NULL;
--static GHashTable *fd_hash = NULL;
-+/*
-+ * The backend will use at most n file descriptors, where n is the
-+ * minimum value between (kern.maxfiles * CFG_GLOBAL_FILE_RESERVE_RATIO)
-+ * and (kern.maxfilesperproc - CFG_SELF_FILE_RESERVE).
-+ */
-+#define CFG_GLOBAL_FILE_RESERVE_RATIO 0.7
-+#define CFG_SELF_FILE_RESERVE 200
-
--static GSList *exist_list = NULL;
-+/*
-+ * If a SubMonitor or FileMonitor is not supported by kqueue and has
-+ * to be polled, the backend will re-attempt to enable kqueue
-+ * notification every n polls.
-+ */
-+#define CFG_UNSUPPORTED_SMON_KQUEUE_RETRY_FREQUENCY 10
-+#define CFG_UNSUPPORTED_FMON_KQUEUE_RETRY_FREQUENCY 10
-
--static GList *new_subs = NULL;
--G_LOCK_DEFINE_STATIC(new_subs);
--static GList *removed_subs = NULL;
--G_LOCK_DEFINE_STATIC(removed_subs);
-+/*
-+ * The various poll intervals, in milliseconds. The default interval
-+ * for each poller is based on the poller's expected usage.
-+ */
-+#define CFG_MISSING_SMON_POLL_INTERVAL 1000
-+#define CFG_UNSUPPORTED_SMON_POLL_INTERVAL 3000
-+#define CFG_UNSUPPORTED_FMON_POLL_INTERVAL 3000
-+
-+/*** end of tunable constants ************************************************/
-+
-+#define VN_NOTE_CHANGED (NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK)
-+#define VN_NOTE_DELETED (NOTE_DELETE | NOTE_REVOKE)
-+#define VN_NOTE_ALL (VN_NOTE_CHANGED | VN_NOTE_DELETED | NOTE_RENAME)
-
--G_LOCK_DEFINE_STATIC(kqueue);
-+/*
-+ * A barebone stat structure, only containing the fields we need.
-+ */
-+typedef struct
-+{
-+ gboolean exists;
-+ ino_t ino;
-+ mode_t mode;
-+ uid_t uid;
-+ gid_t gid;
-+ time_t mtime;
-+ time_t ctime;
-+ off_t size;
-+} MiniStat;
-+
-+typedef void (*HashTableAddFunc) (GHashTable *table,
-+ gpointer item,
-+ gpointer user_data);
-+typedef void (*HashTablePostAddFunc) (GHashTable *table,
-+ gpointer user_data);
-+typedef void (*HashTableRemoveFunc) (GHashTable *table,
-+ gpointer item,
-+ gpointer user_data);
-+typedef void (*HashTablePostRemoveFunc) (GHashTable *table,
-+ gpointer user_data);
-
--static gboolean have_consume_idler = FALSE;
-+typedef struct
-+{
-+ HashTableAddFunc add;
-+ HashTablePostAddFunc post_add;
-+ HashTableRemoveFunc remove;
-+ HashTablePostRemoveFunc post_remove;
-+} HashTableMethods;
-+
-+/*
-+ * A hash table which can be modified while iterating over it.
-+ */
-+typedef struct
-+{
-+ GHashTable *main;
-+ gboolean iterating;
-+ GSList *pending_additions;
-+ GSList *pending_removals;
-+ HashTableMethods *methods;
-+ gpointer user_data;
-+} HashTable;
-+
-+/* the base monitor class */
-+typedef struct _Monitor Monitor;
-+struct _Monitor
-+{
-+ void (*handle_kevent) (Monitor *monitor, struct kevent *event);
-+ char *pathname;
-+ int fd; /* for kqueue */
-+ MiniStat sb; /* for poll */
-+ unsigned int poll_count;
-+};
-+#define MONITOR(ptr) ((Monitor *) ptr)
-
--int kq = -1;
-+typedef enum
-+{
-+ MONITOR_ISDIR = 1 << 0,
-+ MONITOR_ISNOTDIR = 1 << 1
-+} MonitorFlags;
-+#define MONITOR_FLAGS_SHIFT 2
-
--static KQueueData *
--gam_kqueue_data_new(const char *path, int fd)
-+/* monitor for Gamin subscriptions */
-+typedef struct
- {
-- KQueueData *data;
-+ Monitor base;
-+ GList *subs;
-+ HashTable *fmons; /* FileMonitor objects */
-+ HashTable *unsupported_fmons; /* subset of fmons to poll */
-+ gboolean isdir; /* is a directory subscription? */
-+} SubMonitor;
-+#define SUB_MONITOR(ptr) ((SubMonitor *) ptr)
-
-- data = g_new0(KQueueData, 1);
-- data->path = g_strdup(path);
-- data->fd = fd;
-- data->refcount = 1;
-- data->isdir = FALSE;
-- data->subs = NULL;
-- data->dirlist = NULL;
-+typedef enum
-+{
-+ SUB_MONITOR_WAS_MISSING = 1 << MONITOR_FLAGS_SHIFT
-+} SubMonitorFlags;
-
-- return data;
--}
-+/* monitor for files within directory subscriptions */
-+typedef struct
-+{
-+ Monitor base;
-+ SubMonitor *smon; /* the SubMonitor this fmon belongs to */
-+ char *filename; /* pointer into base.pathname */
-+} FileMonitor;
-+#define FILE_MONITOR(ptr) ((FileMonitor *) ptr)
-
--static GSList *
--gam_kqueue_lsdir(const char *path)
-+typedef enum
- {
-- GDir *dir;
-- GSList *lst = NULL;
-- const gchar *entry;
-+ FILE_MONITOR_POSSIBLY_RECREATED = 1 << MONITOR_FLAGS_SHIFT
-+} FileMonitorFlags;
-
-- if (!path)
-- return NULL;
-+typedef void (*PollerFunc) (SubMonitor *smon);
-+typedef struct
-+{
-+ PollerFunc func;
-+ unsigned int interval;
-+ unsigned int timeout_id;
-+ HashTable *smons; /* SubMonitor objects */
-+} Poller;
-+
-+static int kq = -1;
-+
-+static unsigned int open_files = 0;
-+static unsigned int max_open_files = 0;
-+
-+static GHashTable *dir_hash = NULL;
-+static GHashTable *file_hash = NULL;
-+
-+static Poller missing_smon_poller;
-+static Poller unsupported_smon_poller;
-+static Poller unsupported_fmon_poller;
-+
-+static void gam_kqueue_hash_table_default_add_cb (GHashTable *table,
-+ gpointer item,
-+ gpointer user_data);
-+static void gam_kqueue_hash_table_default_remove_cb (GHashTable *table,
-+ gpointer item,
-+ gpointer user_data);
-+
-+static void gam_kqueue_poller_post_add_cb (GHashTable *table, Poller *poller);
-+static void gam_kqueue_poller_post_remove_cb (GHashTable *table, Poller *poller);
-+
-+static HashTableMethods poller_hash_table_methods =
-+ {
-+ gam_kqueue_hash_table_default_add_cb,
-+ (HashTablePostAddFunc) gam_kqueue_poller_post_add_cb,
-+ gam_kqueue_hash_table_default_remove_cb,
-+ (HashTablePostRemoveFunc) gam_kqueue_poller_post_remove_cb
-+ };
-+
-+static void gam_kqueue_sub_monitor_add_fmon_cb (GHashTable *table,
-+ FileMonitor *fmon,
-+ SubMonitor *smon);
-+static void gam_kqueue_sub_monitor_remove_fmon_cb (GHashTable *table,
-+ FileMonitor *fmon,
-+ SubMonitor *smon);
-+
-+static HashTableMethods sub_monitor_fmons_hash_table_methods =
-+ {
-+ (HashTableAddFunc) gam_kqueue_sub_monitor_add_fmon_cb,
-+ (HashTablePostAddFunc) NULL,
-+ (HashTableRemoveFunc) gam_kqueue_sub_monitor_remove_fmon_cb,
-+ (HashTablePostRemoveFunc) NULL
-+ };
-+
-+static void gam_kqueue_sub_monitor_post_add_unsupported_fmon_cb (GHashTable *table,
-+ SubMonitor *smon);
-+static void gam_kqueue_sub_monitor_post_remove_unsupported_fmon_cb (GHashTable *table,
-+ SubMonitor *smon);
-+
-+static HashTableMethods sub_monitor_unsupported_fmons_hash_table_methods =
-+ {
-+ gam_kqueue_hash_table_default_add_cb,
-+ (HashTablePostAddFunc) gam_kqueue_sub_monitor_post_add_unsupported_fmon_cb,
-+ gam_kqueue_hash_table_default_remove_cb,
-+ (HashTablePostRemoveFunc) gam_kqueue_sub_monitor_post_remove_unsupported_fmon_cb
-+ };
-+
-+static void gam_kqueue_sub_monitor_emit_event (SubMonitor *smon,
-+ GaminEventType event,
-+ SubMonitorFlags flags);
-+static void gam_kqueue_sub_monitor_handle_kevent (Monitor *mon,
-+ struct kevent *event);
-+
-+static FileMonitor *gam_kqueue_file_monitor_new (SubMonitor *smon,
-+ const char *filename,
-+ FileMonitorFlags *flags);
-+static void gam_kqueue_file_monitor_free (FileMonitor *fmon);
-+static void gam_kqueue_file_monitor_emit_event (FileMonitor *fmon,
-+ GaminEventType event,
-+ FileMonitorFlags flags);
-+static void gam_kqueue_file_monitor_handle_kevent (Monitor *mon, struct kevent *event);
-
-- dir = g_dir_open(path, 0, NULL);
-- if (!dir)
-- return NULL;
-+/*** helpers *****************************************************************/
-
-- entry = g_dir_read_name(dir);
-+static void
-+gam_kqueue_mini_lstat (const char *pathname, MiniStat *mini_sb)
-+{
-+ struct stat sb;
-+
-+ if (lstat(pathname, &sb) < 0)
-+ memset(mini_sb, 0, sizeof(*mini_sb));
-+ else
-+ {
-+ mini_sb->exists = TRUE;
-+ mini_sb->ino = sb.st_ino;
-+ mini_sb->mode = sb.st_mode;
-+ mini_sb->uid = sb.st_uid;
-+ mini_sb->gid = sb.st_gid;
-+ mini_sb->mtime = sb.st_mtime;
-+ mini_sb->ctime = sb.st_ctime;
-+ mini_sb->size = sb.st_size;
-+ }
-+}
-+
-+static gboolean
-+gam_kqueue_differs (const MiniStat *sb1, const MiniStat *sb2)
-+{
-+ return sb1->mtime != sb2->mtime
-+ || sb1->ctime != sb2->ctime
-+ || sb1->size != sb2->size
-+ || sb1->mode != sb2->mode
-+ || sb1->uid != sb2->uid
-+ || sb1->gid != sb2->gid
-+ || sb1->ino != sb2->ino;
-+}
-
-- while (entry) {
-- lst = g_slist_prepend(lst, g_strdup(entry));
-- entry = g_dir_read_name(dir);
-+static gboolean
-+gam_kqueue_isdir (const char *pathname, MonitorFlags flags)
-+{
-+ if ((flags & MONITOR_ISDIR) != 0)
-+ return TRUE;
-+ else if ((flags & MONITOR_ISNOTDIR) != 0)
-+ return FALSE;
-+ else
-+ {
-+ struct stat sb;
-+ return lstat(pathname, &sb) >= 0 && (sb.st_mode & S_IFDIR) != 0;
- }
-+}
-
-- g_dir_close(dir);
-+static gboolean
-+gam_kqueue_get_uint_sysctl (const char *name, unsigned int *value)
-+{
-+ unsigned int value_len = sizeof(*value);
-
-- return lst;
-+ if (sysctlbyname(name, value, &value_len, NULL, 0) < 0)
-+ {
-+ gam_error(DEBUG_INFO, "unable to retrieve %s: %s\n", name, g_strerror(errno));
-+ return FALSE;
-+ }
-+ else
-+ return TRUE;
-+}
-+
-+/*** HashTable ***************************************************************/
-+
-+static HashTable *
-+gam_kqueue_hash_table_new (GHashTable *main,
-+ HashTableMethods *methods,
-+ gpointer user_data)
-+{
-+ HashTable *table;
-+
-+ table = g_new0(HashTable, 1);
-+ table->main = main;
-+ table->methods = methods;
-+ table->user_data = user_data;
-+
-+ return table;
- }
-
- static void
--gam_kqueue_cmplst(GSList *lst1, GSList *lst2, GSList **added, GSList **deleted)
-+gam_kqueue_hash_table_default_add_cb (GHashTable *table,
-+ gpointer item,
-+ gpointer user_data)
- {
-- int found;
-- GSList *l;
-+ g_hash_table_insert(table, item, GINT_TO_POINTER(TRUE));
-+}
-
-- if (!lst1 && !lst2)
-- return;
-+static void
-+gam_kqueue_hash_table_default_remove_cb (GHashTable *table,
-+ gpointer item,
-+ gpointer user_data)
-+{
-+ g_hash_table_remove(table, item);
-+}
-
-- if (!lst1) {
-- *added = g_slist_copy(lst2);
-- return;
-+static void
-+gam_kqueue_hash_table_add (HashTable *table, gpointer item)
-+{
-+ if (table->iterating)
-+ table->pending_additions = g_slist_prepend(table->pending_additions, item);
-+ else
-+ {
-+ table->methods->add(table->main, item, table->user_data);
-+ if (table->methods->post_add)
-+ table->methods->post_add(table->main, table->user_data);
- }
-+}
-
-- if (!lst2) {
-- *deleted = g_slist_copy(lst1);
-- return;
-+static void
-+gam_kqueue_hash_table_remove (HashTable *table, gpointer item)
-+{
-+ if (table->iterating)
-+ table->pending_removals = g_slist_prepend(table->pending_removals, item);
-+ else
-+ {
-+ table->methods->remove(table->main, item, table->user_data);
-+ if (table->methods->post_remove)
-+ table->methods->post_remove(table->main, table->user_data);
- }
-+}
-+
-+static void
-+gam_kqueue_hash_table_foreach (HashTable *table,
-+ GHFunc func,
-+ gpointer user_data)
-+{
-+ g_assert(table->iterating == FALSE);
-+
-+ table->iterating = TRUE;
-+ g_hash_table_foreach(table->main, func, user_data);
-+ table->iterating = FALSE;
-+
-+ if (table->pending_additions)
-+ {
-+ GSList *l;
-
-- for (l = lst1; l; l = l->next) {
-- found = 0;
-- if (g_slist_find_custom(lst2, l->data, (GCompareFunc)strcmp)) {
-- found = 1;
-- }
-- if (found == 0) {
-- *deleted = g_slist_prepend(*deleted, l->data);
-- }
-+ for (l = table->pending_additions; l != NULL; l = l->next)
-+ table->methods->add(table->main, l->data, table->user_data);
-+
-+ g_slist_free(table->pending_additions);
-+ table->pending_additions = NULL;
-+
-+ if (table->methods->post_add)
-+ table->methods->post_add(table->main, table->user_data);
- }
-
-- for (l = lst2; l; l = l->next) {
-- found = 0;
-- if (g_slist_find_custom(lst1, l->data, (GCompareFunc)strcmp)) {
-- found = 1;
-- }
-- if (found == 0) {
-- *added = g_slist_prepend(*added, l->data);
-- }
-+ if (table->pending_removals)
-+ {
-+ GSList *l;
-+
-+ for (l = table->pending_removals; l != NULL; l = l->next)
-+ table->methods->remove(table->main, l->data, table->user_data);
-+
-+ g_slist_free(table->pending_removals);
-+ table->pending_removals = NULL;
-+
-+ if (table->methods->post_remove)
-+ table->methods->post_remove(table->main, table->user_data);
- }
- }
-
- static void
--gam_kqueue_data_free(KQueueData * data)
-+gam_kqueue_hash_table_destroy (HashTable *table)
- {
-- g_free(data->path);
-- if (data->dirlist) {
-- g_slist_foreach(data->dirlist, (GFunc)g_free, NULL);
-- g_slist_free(data->dirlist);
-- }
-- if (data->subs) {
-- g_list_free(data->subs);
-+ g_assert(table->iterating == FALSE);
-+
-+ g_hash_table_destroy(table->main);
-+ g_free(table);
-+}
-+
-+/*** Poller ******************************************************************/
-+
-+static void
-+gam_kqueue_poller_init (Poller *poller, PollerFunc func, unsigned int interval)
-+{
-+ poller->func = func;
-+ poller->interval = interval;
-+ poller->timeout_id = 0;
-+ poller->smons = gam_kqueue_hash_table_new(g_hash_table_new(g_direct_hash, g_direct_equal),
-+ &poller_hash_table_methods,
-+ poller);
-+}
-+
-+static void
-+gam_kqueue_poller_foreach_cb (SubMonitor *smon,
-+ gpointer unused,
-+ Poller *poller)
-+{
-+ poller->func(smon);
-+}
-+
-+static gboolean
-+gam_kqueue_poller_timeout_cb (Poller *poller)
-+{
-+ gam_kqueue_hash_table_foreach(poller->smons, (GHFunc) gam_kqueue_poller_foreach_cb, poller);
-+
-+ return TRUE; /* keep source */
-+}
-+
-+static void
-+gam_kqueue_poller_post_add_cb (GHashTable *table, Poller *poller)
-+{
-+ if (! poller->timeout_id)
-+ poller->timeout_id = g_timeout_add(poller->interval, (GSourceFunc) gam_kqueue_poller_timeout_cb, poller);
-+}
-+
-+static void
-+gam_kqueue_poller_post_remove_cb (GHashTable *table, Poller *poller)
-+{
-+ if (g_hash_table_size(table) == 0 && poller->timeout_id)
-+ {
-+ g_source_remove(poller->timeout_id);
-+ poller->timeout_id = 0;
- }
-- g_free(data);
- }
-
- static void
--gam_kqueue_add_rm_handler(const char *path, GamSubscription *sub, gboolean added, gboolean was_missing)
-+gam_kqueue_poller_add_sub_monitor (Poller *poller, SubMonitor *smon)
-+{
-+ gam_kqueue_hash_table_add(poller->smons, smon);
-+}
-+
-+static void
-+gam_kqueue_poller_remove_sub_monitor (Poller *poller, SubMonitor *smon)
- {
-- KQueueData *data;
-- struct kevent ev[1];
-- int isdir = 0;
-- int fd;
-+ gam_kqueue_hash_table_remove(poller->smons, smon);
-+}
-
-- G_LOCK(kqueue);
-+/*** Monitor *****************************************************************/
-
-- isdir = g_file_test(path, G_FILE_TEST_IS_DIR);
-- if (gam_subscription_is_dir(sub)) {
-- data = g_hash_table_lookup(dir_path_hash, path);
-+static gboolean
-+gam_kqueue_monitor_enable_kqueue (Monitor *mon)
-+{
-+ struct kevent ev[1];
-+
-+ if (open_files == max_open_files)
-+ {
-+ GAM_DEBUG(DEBUG_INFO, "cannot open %s (max_open_files limit reached), falling back to poll\n", mon->pathname);
-+ return FALSE;
- }
-- else {
-- data = g_hash_table_lookup(file_path_hash, path);
-+
-+ mon->fd = open(mon->pathname, O_RDONLY | O_NOFOLLOW);
-+ if (mon->fd < 0)
-+ {
-+ GAM_DEBUG(DEBUG_INFO, "cannot open %s (%s), falling back to poll\n", mon->pathname, g_strerror(errno));
-+ return FALSE;
- }
-
-- if (added) {
-- GList *subs;
-+ EV_SET(ev, mon->fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, VN_NOTE_ALL, 0, mon);
-+ if (kevent(kq, ev, G_N_ELEMENTS(ev), NULL, 0, NULL) < 0)
-+ {
-+ GAM_DEBUG(DEBUG_INFO, "cannot enable kqueue notification for %s (%s), falling back to poll\n", mon->pathname, g_strerror(errno));
-
-- subs = NULL;
-- subs = g_list_append(subs, sub);
-+ close(mon->fd);
-+ mon->fd = -1;
-
-- if (data != NULL) {
-- data->refcount++;
-- data->subs = g_list_prepend(data->subs, sub);
-- G_UNLOCK(kqueue);
-- GAM_DEBUG(DEBUG_INFO, "kqueue updated refcount\n");
-- if (!was_missing) {
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_EXISTS, subs, 1);
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_ENDEXISTS, subs, 1);
-- }
-- else {
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_CREATED, subs, 1);
-- }
-- return;
-- }
-+ return FALSE;
-+ }
-
-- if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
-- data = gam_kqueue_data_new(path, -1);
-- data->subs = g_list_prepend(data->subs, sub);
-- exist_list = g_slist_append(exist_list, data);
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_DELETED, subs, 1);
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_ENDEXISTS, subs, 1);
-- G_UNLOCK(kqueue);
-- return;
-- }
-+ open_files++;
-+ return TRUE;
-+}
-
-- fd = open(path, O_RDONLY);
-+static void
-+gam_kqueue_monitor_set_unsupported (Monitor *mon)
-+{
-+ if (mon->fd >= 0)
-+ {
-+ close(mon->fd);
-+ mon->fd = -1;
-
-- if (fd < 0) {
-- G_UNLOCK(kqueue);
-- return;
-- }
-+ open_files--;
-+ }
-
-- EV_SET(ev, fd, EVFILT_VNODE,
-- EV_ADD | EV_ENABLE | EV_CLEAR, VN_NOTE_ALL, 0, 0);
-- kevent(kq, ev, 1, NULL, 0, NULL);
-+ gam_kqueue_mini_lstat(mon->pathname, &mon->sb);
-+}
-
-- data = gam_kqueue_data_new(path, fd);
-- data->subs = g_list_prepend(data->subs, sub);
-+static void
-+gam_kqueue_monitor_free (Monitor *mon)
-+{
-+ g_free(mon->pathname);
-+ if (mon->fd >= 0)
-+ {
-+ close(mon->fd);
-+ open_files--;
-+ }
-+ g_free(mon);
-+}
-
-- if (!was_missing) {
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_EXISTS, subs, 1);
-- }
-- else {
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_CREATED, subs, 1);
-- }
-- if (gam_subscription_is_dir(sub) && isdir) {
-- GSList *l;
-+/*** SubMonitor **************************************************************/
-
-- data->isdir = TRUE;
-- data->dirlist = gam_kqueue_lsdir(path);
-+static void
-+gam_kqueue_sub_monitor_init_fmons (SubMonitor *smon)
-+{
-+ smon->fmons = gam_kqueue_hash_table_new(g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) gam_kqueue_file_monitor_free),
-+ &sub_monitor_fmons_hash_table_methods,
-+ smon);
-+ smon->unsupported_fmons = gam_kqueue_hash_table_new(g_hash_table_new(g_direct_hash, g_direct_equal),
-+ &sub_monitor_unsupported_fmons_hash_table_methods,
-+ smon);
-+}
-
-- for (l = data->dirlist; l; l = l->next) {
-- char *tmpentry;
-+static SubMonitor *
-+gam_kqueue_sub_monitor_new (GamSubscription *sub)
-+{
-+ SubMonitor *smon;
-+ Monitor *mon;
-
-- tmpentry = g_build_filename(path, l->data, NULL);
-- if (!was_missing) {
-- gam_server_emit_event (tmpentry,
-- g_file_test(tmpentry, G_FILE_TEST_IS_DIR),
-- GAMIN_EVENT_EXISTS, subs, 1);
-- }
-- g_free(tmpentry);
-- }
-- }
-+ smon = g_new0(SubMonitor, 1);
-
-- if (!was_missing) {
-- gam_server_emit_event (path, isdir, GAMIN_EVENT_ENDEXISTS, subs, 1);
-- }
-+ mon = MONITOR(smon);
-+ mon->handle_kevent = gam_kqueue_sub_monitor_handle_kevent;
-+ mon->pathname = g_strdup(gam_subscription_get_path(sub));
-+ mon->fd = -1;
-
-- g_hash_table_insert(fd_hash, GINT_TO_POINTER(data->fd), data);
-- if (data->isdir) {
-- g_hash_table_insert(dir_path_hash, data->path, data);
-- }
-- else {
-- g_hash_table_insert(file_path_hash, data->path, data);
-- }
-+ smon->isdir = gam_subscription_is_dir(sub);
-+ gam_kqueue_sub_monitor_init_fmons(smon);
-
-- if (subs)
-- g_list_free(subs);
-+ return smon;
-+}
-
-- GAM_DEBUG(DEBUG_INFO, "added kqueue watch for %s\n", path);
-- } else {
-+static void
-+gam_kqueue_sub_monitor_clear_fmons (SubMonitor *smon)
-+{
-+ if (g_hash_table_size(smon->unsupported_fmons->main) > 0)
-+ gam_kqueue_poller_remove_sub_monitor(&unsupported_fmon_poller, smon);
-
-- if (!data) {
-- G_UNLOCK(kqueue);
-- return;
-- }
-+ gam_kqueue_hash_table_destroy(smon->unsupported_fmons);
-+ gam_kqueue_hash_table_destroy(smon->fmons);
-+}
-
-- if (g_list_find (data->subs, sub)) {
-- data->subs = g_list_remove_all (data->subs, sub);
-- }
-- data->refcount--;
-- GAM_DEBUG(DEBUG_INFO, "kqueue decremeneted refcount for %s\n", path);
-+static void
-+gam_kqueue_sub_monitor_free (SubMonitor *smon)
-+{
-+ gam_kqueue_poller_remove_sub_monitor(&missing_smon_poller, smon);
-+ gam_kqueue_poller_remove_sub_monitor(&unsupported_smon_poller, smon);
-+ /* unsupported_dirs_poller is handled by _clear_fmons() below */
-+
-+ gam_kqueue_sub_monitor_clear_fmons(smon);
-+ gam_kqueue_monitor_free(MONITOR(smon));
-+}
-
-- if (data->refcount == 0) {
-- GList *l;
-+static void
-+gam_kqueue_sub_monitor_add_fmon_cb (GHashTable *table,
-+ FileMonitor *fmon,
-+ SubMonitor *smon)
-+{
-+ g_hash_table_replace(table, fmon->filename, fmon);
-+}
-
-- close(data->fd);
-- l = data->subs;
-- for (l = l; l; l = l->next) {
-- GamSubscription *sub = l->data;
-- gam_kqueue_remove_subscription (sub);
-- }
-- GAM_DEBUG(DEBUG_INFO, "removed kqueue watch for %s\n", data->path);
-- if (data->isdir) {
-- g_hash_table_remove(dir_path_hash, data->path);
-- }
-- else {
-- g_hash_table_remove(file_path_hash, data->path);
-- }
-- g_hash_table_remove(fd_hash, GINT_TO_POINTER(data->fd));
-- gam_kqueue_data_free(data);
-- }
-- }
-- G_UNLOCK(kqueue);
-+static void
-+gam_kqueue_sub_monitor_remove_fmon_cb (GHashTable *table,
-+ FileMonitor *fmon,
-+ SubMonitor *smon)
-+{
-+ g_hash_table_remove(table, fmon->filename);
-+}
-+
-+static void
-+gam_kqueue_sub_monitor_post_add_unsupported_fmon_cb (GHashTable *table,
-+ SubMonitor *smon)
-+{
-+ gam_kqueue_poller_add_sub_monitor(&unsupported_fmon_poller, smon);
- }
-
--static GaminEventType kqueue_event_to_gamin_event (int mask)
-+static void
-+gam_kqueue_sub_monitor_post_remove_unsupported_fmon_cb (GHashTable *table,
-+ SubMonitor *smon)
- {
-- if ((mask & VN_NOTE_CHANGED) != 0)
-- return GAMIN_EVENT_CHANGED;
-- else if ((mask & NOTE_DELETE) != 0)
-- return GAMIN_EVENT_DELETED;
-- else if ((mask & NOTE_REVOKE) != 0)
-- return GAMIN_EVENT_ENDEXISTS;
-- else if ((mask & NOTE_RENAME) != 0)
-- return GAMIN_EVENT_MOVED;
-- else
-- return GAMIN_EVENT_UNKNOWN;
-+ if (g_hash_table_size(table) == 0)
-+ gam_kqueue_poller_remove_sub_monitor(&unsupported_fmon_poller, smon);
- }
-
--static void gam_kqueue_emit_event (KQueueData *data, struct kevent *event)
-+static void
-+gam_kqueue_sub_monitor_set_missing (SubMonitor *smon)
- {
-- GaminEventType gevent;
-- int isdir = 0;
-- char *event_path;
-+ Monitor *mon = MONITOR(smon);
-
-- if (!data||!event)
-- return;
-+ if (mon->fd >= 0)
-+ {
-+ close(mon->fd);
-+ mon->fd = -1;
-
-- gevent = kqueue_event_to_gamin_event (event->fflags);
-+ open_files--;
-+ }
-
-- if (gevent == GAMIN_EVENT_UNKNOWN) {
-- return;
-- }
-+ /*
-+ * A removed directory will normally not contain files, but we must
-+ * not assume it (we might receive events out of order, etc). We
-+ * therefore check if files are remaining, and if yes, clear them.
-+ */
-+ if (g_hash_table_size(smon->fmons->main) > 0)
-+ {
-+ gam_kqueue_sub_monitor_clear_fmons(smon);
-+ gam_kqueue_sub_monitor_init_fmons(smon);
-+ }
-
-- isdir = g_file_test(data->path, G_FILE_TEST_IS_DIR);
-+ gam_kqueue_poller_remove_sub_monitor(&unsupported_smon_poller, smon);
-+ gam_kqueue_poller_add_sub_monitor(&missing_smon_poller, smon);
-+}
-
-- if (gevent == GAMIN_EVENT_CHANGED && data->isdir) {
-- GSList *dirlist = NULL, *added = NULL, *deleted = NULL;
-- GSList *l;
--
-- dirlist = gam_kqueue_lsdir(data->path);
-- gam_kqueue_cmplst(data->dirlist, dirlist, &added, &deleted);
-- if (added || deleted) {
-- for (l = deleted; l; l = l->next) {
-- data->dirlist = g_slist_remove(data->dirlist, l->data);
-- event_path = g_build_filename(data->path, l->data, NULL);
-- g_free(l->data);
-- isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
--
-- GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED) , event_path);
--
-- gam_server_emit_event (event_path, isdir,
-- GAMIN_EVENT_DELETED, data->subs, 1);
-- g_free(event_path);
-- }
-+static void
-+gam_kqueue_sub_monitor_set_unsupported (SubMonitor *smon)
-+{
-+ Monitor *mon = MONITOR(smon);
-
-- for (l = added; l; l = l->next) {
-- dirlist = g_slist_remove(dirlist, l->data);
-- data->dirlist = g_slist_prepend(data->dirlist,
-- g_strdup(l->data));
-- event_path = g_build_filename(data->path, l->data, NULL);
-- g_free(l->data);
-- isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
--
-- GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED) , event_path);
--
-- gam_server_emit_event (event_path, isdir,
-- GAMIN_EVENT_CREATED, data->subs, 1);
-- g_free(event_path);
-- }
-+ gam_kqueue_monitor_set_unsupported(mon);
-+ gam_kqueue_poller_add_sub_monitor(&unsupported_smon_poller, smon);
-+}
-
-- if (added)
-- g_slist_free(added);
-- if (deleted)
-- g_slist_free(deleted);
-- }
-+static void
-+gam_kqueue_sub_monitor_enable_notification (SubMonitor *smon,
-+ SubMonitorFlags flags)
-+{
-+ Monitor *mon = MONITOR(smon);
-+ gboolean exists;
-+
-+ /* we first send CREATED or EXISTS/DELETED+ENDEXISTS events */
-+
-+ if ((flags & SUB_MONITOR_WAS_MISSING) != 0)
-+ exists = TRUE;
-+ else
-+ {
-+ struct stat sb;
-+
-+ exists = lstat(mon->pathname, &sb) >= 0;
-+ flags |= (exists && (sb.st_mode & S_IFDIR) != 0) ? MONITOR_ISDIR : MONITOR_ISNOTDIR;
-+ }
-
-- if (dirlist) {
-- g_slist_foreach(dirlist, (GFunc)g_free, NULL);
-- g_slist_free(dirlist);
-+ if (exists)
-+ {
-+ GaminEventType gevent;
-+
-+ gevent = (flags & SUB_MONITOR_WAS_MISSING) != 0 ? GAMIN_EVENT_CREATED : GAMIN_EVENT_EXISTS;
-+ gam_kqueue_sub_monitor_emit_event(smon, gevent, flags);
-+
-+ if (smon->isdir && (flags & MONITOR_ISDIR) != 0)
-+ {
-+ GDir *dir;
-+ GError *err = NULL;
-+
-+ dir = g_dir_open(mon->pathname, 0, &err);
-+ if (dir)
-+ {
-+ const char *filename;
-+
-+ while ((filename = g_dir_read_name(dir)))
-+ {
-+ FileMonitor *fmon;
-+ FileMonitorFlags fmon_flags;
-+
-+ fmon = gam_kqueue_file_monitor_new(smon, filename, &fmon_flags);
-+ gam_kqueue_file_monitor_emit_event(fmon, gevent, fmon_flags);
-+ }
-+
-+ g_dir_close(dir);
-+ }
-+ else
-+ {
-+ GAM_DEBUG(DEBUG_INFO, "unable to open directory %s: %s\n", mon->pathname, err->message);
-+ g_error_free(err);
- }
-- return;
-- }
-- else {
-- event_path = g_strdup (data->path);
- }
-
-- isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
-+ if ((flags & SUB_MONITOR_WAS_MISSING) == 0)
-+ gam_kqueue_sub_monitor_emit_event(smon, GAMIN_EVENT_ENDEXISTS, flags);
-+ }
-+ else
-+ {
-+ gam_kqueue_sub_monitor_emit_event(smon, GAMIN_EVENT_DELETED, flags);
-+ gam_kqueue_sub_monitor_emit_event(smon, GAMIN_EVENT_ENDEXISTS, flags);
-
-- GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(gevent) , event_path);
-+ return;
-+ }
-+
-+ /* then we enable kqueue notification, falling back to poll if necessary */
-
-- gam_server_emit_event (event_path, isdir, gevent, data->subs, 1);
-+ if (! gam_kqueue_monitor_enable_kqueue(mon))
-+ gam_kqueue_sub_monitor_set_unsupported(smon);
-+}
-
-- g_free (event_path);
-+static void
-+gam_kqueue_sub_monitor_handle_directory_change_removal_cb (const char *filename,
-+ FileMonitor *fmon,
-+ GHashTable *filenames)
-+{
-+ if (! g_hash_table_lookup(filenames, filename))
-+ gam_kqueue_file_monitor_emit_event(fmon, GAMIN_EVENT_DELETED, MONITOR_ISNOTDIR);
- }
-
--static gboolean
--gam_kqueue_exist_check (gpointer user_data)
-+static void
-+gam_kqueue_sub_monitor_handle_directory_change (SubMonitor *smon,
-+ gboolean isdir)
- {
-- GSList *l, *tmplst;
-- KQueueData *data;
-+ Monitor *mon = MONITOR(smon);
-+ GHashTable *filenames;
-
-- tmplst = g_slist_copy(exist_list);
-+ filenames = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-
-- for (l = tmplst; l; l = l->next) {
-- data = l->data;
-+ if (isdir) /* do not follow symbolic links */
-+ {
-+ GDir *dir;
-+ GError *err = NULL;
-+
-+ dir = g_dir_open(mon->pathname, 0, &err);
-+ if (dir)
-+ {
-+ const char *filename;
-+
-+ while ((filename = g_dir_read_name(dir)))
-+ {
-+ g_hash_table_insert(filenames, g_strdup(filename), GINT_TO_POINTER(TRUE));
-+
-+ /* handle file creation */
-+ if (! g_hash_table_lookup(smon->fmons->main, filename))
-+ {
-+ FileMonitor *fmon;
-+ FileMonitorFlags fmon_flags;
-+
-+ fmon = gam_kqueue_file_monitor_new(smon, filename, &fmon_flags);
-+ gam_kqueue_file_monitor_emit_event(fmon, GAMIN_EVENT_CREATED, fmon_flags);
-+ }
-+ }
-+
-+ g_dir_close(dir);
-+ }
-+ else
-+ {
-+ GAM_DEBUG(DEBUG_INFO, "unable to open directory %s: %s\n", mon->pathname, err->message);
-+ g_error_free(err);
-+ }
-+ }
-
-- if (g_file_test(data->path, G_FILE_TEST_EXISTS)) {
-- /* The subs list is guaranteed to have only one entry. */
-- GamSubscription *sub = data->subs->data;
-+ /*
-+ * Handle deleted files (they are also handled at the FileMonitor
-+ * level, but we must use whichever event comes first).
-+ */
-+ gam_kqueue_hash_table_foreach(smon->fmons, (GHFunc) gam_kqueue_sub_monitor_handle_directory_change_removal_cb, filenames);
-+ g_hash_table_destroy(filenames);
-+}
-
-- exist_list = g_slist_remove(exist_list, data);
-- gam_kqueue_add_rm_handler(data->path, sub, TRUE, TRUE);
-- gam_kqueue_data_free(data);
-+static void
-+gam_kqueue_sub_monitor_emit_event (SubMonitor *smon,
-+ GaminEventType event,
-+ SubMonitorFlags flags)
-+{
-+ Monitor *mon = MONITOR(smon);
-+ gboolean isdir;
-+
-+ isdir = gam_kqueue_isdir(mon->pathname, flags);
-+
-+ switch (event)
-+ {
-+ case GAMIN_EVENT_CHANGED:
-+ if (smon->isdir)
-+ {
-+ gam_kqueue_sub_monitor_handle_directory_change(smon, isdir);
-+ return;
- }
-+ break;
-+
-+ case GAMIN_EVENT_DELETED:
-+ case GAMIN_EVENT_MOVED:
-+ gam_kqueue_sub_monitor_set_missing(smon);
-+ break;
- }
-
-- if (tmplst)
-- g_slist_free(tmplst);
-+ gam_server_emit_event(mon->pathname, isdir, event, smon->subs, 1);
-+}
-
-- return TRUE;
-+static void
-+gam_kqueue_sub_monitor_handle_kevent (Monitor *mon, struct kevent *event)
-+{
-+ SubMonitor *smon = SUB_MONITOR(mon);
-+
-+ if ((event->flags & EV_ERROR) != 0)
-+ {
-+ /* kqueue failed, fallback to poll */
-+ GAM_DEBUG(DEBUG_INFO, "kqueue failed for %s, falling back to poll\n", mon->pathname);
-+ gam_kqueue_sub_monitor_set_unsupported(smon);
-+ return;
-+ }
-+
-+ /*
-+ * kevent aggregates events, so we must handle a fflags with
-+ * multiple event bits set.
-+ */
-+ if ((event->fflags & VN_NOTE_CHANGED) != 0)
-+ gam_kqueue_sub_monitor_emit_event(smon, GAMIN_EVENT_CHANGED, 0);
-+
-+ /* emitting the following events will add the smon to the missing poller */
-+ if ((event->fflags & VN_NOTE_DELETED) != 0)
-+ gam_kqueue_sub_monitor_emit_event(smon, GAMIN_EVENT_DELETED, MONITOR_ISNOTDIR);
-+ else if ((event->fflags & NOTE_RENAME) != 0)
-+ gam_kqueue_sub_monitor_emit_event(smon, GAMIN_EVENT_MOVED, MONITOR_ISNOTDIR);
- }
-
--static gboolean
--gam_kqueue_event_handler (gpointer user_data)
-+/*** FileMonitor *************************************************************/
-+
-+static void
-+gam_kqueue_file_monitor_set_unsupported (FileMonitor *fmon)
- {
-- KQueueData *data;
-- struct kevent ev[1];
-- struct timespec timeout = { 0, 0 };
-- int fd, i, nevents;
--
-- G_LOCK(kqueue);
--
-- GAM_DEBUG(DEBUG_INFO, "gam_kqueue_event_handler()\n");
--
-- nevents = kevent(kq, NULL, 0, ev, 1, &timeout);
-- if (nevents == -1)
-- return FALSE;
-- for (i = 0; i < nevents; i++) {
-- fd = ev[i].ident;
--
-- data = g_hash_table_lookup (fd_hash, GINT_TO_POINTER(fd));
-- if (!data) {
-- GAM_DEBUG(DEBUG_INFO, "kqueue can't find fd %d\n", fd);
-- GAM_DEBUG(DEBUG_INFO, "weird things have happened to kqueue.\n");
-- } else {
-- gam_kqueue_emit_event (data, &ev[i]);
-- }
-+ Monitor *mon = MONITOR(fmon);
-
-+ gam_kqueue_monitor_set_unsupported(mon);
-+ gam_kqueue_hash_table_add(fmon->smon->unsupported_fmons, fmon);
-+}
-+
-+static FileMonitor *
-+gam_kqueue_file_monitor_new (SubMonitor *smon,
-+ const char *filename,
-+ FileMonitorFlags *flags)
-+{
-+ FileMonitor *fmon;
-+ Monitor *mon;
-+
-+ fmon = g_new0(FileMonitor, 1);
-+
-+ mon = MONITOR(fmon);
-+ mon->handle_kevent = gam_kqueue_file_monitor_handle_kevent;
-+ mon->pathname = g_build_filename(MONITOR(smon)->pathname, filename, NULL);
-+ mon->fd = -1;
-+
-+ fmon->smon = smon;
-+ fmon->filename = strrchr(mon->pathname, G_DIR_SEPARATOR);
-+ fmon->filename = fmon->filename ? fmon->filename + 1 : mon->pathname;
-+
-+ gam_kqueue_hash_table_add(fmon->smon->fmons, fmon);
-+
-+ if (gam_kqueue_monitor_enable_kqueue(mon))
-+ *flags = 0;
-+ else
-+ {
-+ gam_kqueue_file_monitor_set_unsupported(fmon);
-+ *flags = (mon->sb.mode & S_IFDIR) != 0 ? MONITOR_ISDIR : MONITOR_ISNOTDIR;
- }
-
-- G_UNLOCK(kqueue);
-+ return fmon;
-+}
-
-- return TRUE;
-+static void
-+gam_kqueue_file_monitor_free (FileMonitor *fmon)
-+{
-+ gam_kqueue_monitor_free(MONITOR(fmon));
- }
-
--static gboolean
--gam_kqueue_consume_subscriptions_real(gpointer data)
-+static void
-+gam_kqueue_file_monitor_emit_event (FileMonitor *fmon,
-+ GaminEventType event,
-+ FileMonitorFlags flags)
- {
-- GList *subs, *l;
-+ Monitor *mon = MONITOR(fmon);
-+ struct stat sb;
-+ gboolean isdir;
-+ gboolean stat_done;
-+ gboolean stat_succeeded;
-+
-+ if ((flags & MONITOR_ISDIR) == 0 && (flags & MONITOR_ISNOTDIR) == 0)
-+ {
-+ stat_done = TRUE;
-+ stat_succeeded = lstat(mon->pathname, &sb) >= 0;
-+ isdir = stat_succeeded && (sb.st_mode & S_IFDIR) != 0;
-+ }
-+ else
-+ {
-+ stat_done = FALSE;
-+ isdir = (flags & MONITOR_ISDIR) != 0;
-+ }
-+
-+ gam_server_emit_event(mon->pathname, isdir, event, fmon->smon->subs, 1);
-
-- G_LOCK(new_subs);
-- if (new_subs) {
-- subs = new_subs;
-- new_subs = NULL;
-- G_UNLOCK(new_subs);
--
-- for (l = subs; l; l = l->next) {
-- GamSubscription *sub = l->data;
-- GAM_DEBUG(DEBUG_INFO, "called gam_kqueue_add_handler()\n");
-- gam_kqueue_add_rm_handler (gam_subscription_get_path (sub), sub, TRUE, FALSE);
-- }
-+ switch (event)
-+ {
-+ case GAMIN_EVENT_DELETED:
-+ case GAMIN_EVENT_MOVED:
-+ if (mon->fd < 0)
-+ gam_kqueue_hash_table_remove(fmon->smon->unsupported_fmons, fmon);
-+
-+ if ((flags & FILE_MONITOR_POSSIBLY_RECREATED) != 0)
-+ {
-+ if (! stat_done)
-+ stat_succeeded = lstat(mon->pathname, &sb) >= 0;
-+
-+ if (stat_succeeded)
-+ {
-+ FileMonitor *new_fmon;
-+ FileMonitorFlags new_fmon_flags;
-+
-+ /*
-+ * The file exists again. It means that kqueue has
-+ * aggregated a removal+creation into a single event. We
-+ * must therefore create a new fmon and emit a
-+ * GAMIN_EVENT_CREATED event, because
-+ * gam_kqueue_sub_monitor_handle_directory_change() did
-+ * not detect the removal+creation.
-+ */
-
-- } else {
-- G_UNLOCK(new_subs);
-- }
-+ new_fmon = gam_kqueue_file_monitor_new(fmon->smon, fmon->filename, &new_fmon_flags);
-+ gam_kqueue_file_monitor_emit_event(new_fmon, GAMIN_EVENT_CREATED, new_fmon_flags);
-
-- G_LOCK(removed_subs);
-- if (removed_subs) {
-- subs = removed_subs;
-- removed_subs = NULL;
-- G_UNLOCK(removed_subs);
--
-- for (l = subs; l; l = l->next) {
-- GamSubscription *sub = l->data;
-- GAM_DEBUG(DEBUG_INFO, "called gam_kqueue_rm_handler()\n");
-- gam_kqueue_add_rm_handler (gam_subscription_get_path (sub), sub, FALSE, FALSE);
-- }
-- } else {
-- G_UNLOCK(removed_subs);
-+ break; /* do not remove the fmon we've just created */
-+ }
- }
-+
-+ gam_kqueue_hash_table_remove(fmon->smon->fmons, fmon);
-+ break;
-+ }
-+}
-+
-+static void
-+gam_kqueue_file_monitor_handle_kevent (Monitor *mon, struct kevent *event)
-+{
-+ FileMonitor *fmon = FILE_MONITOR(mon);
-+
-+ if ((event->flags & EV_ERROR) != 0)
-+ {
-+ /* kqueue failed, fallback to poll */
-+ GAM_DEBUG(DEBUG_INFO, "kqueue failed for %s, falling back to poll\n", mon->pathname);
-+ gam_kqueue_file_monitor_set_unsupported(fmon);
-+ return;
-+ }
-+
-+ /*
-+ * kevent aggregates events, so we must handle a fflags with
-+ * multiple event bits set.
-+ */
-+
-+ if ((event->fflags & VN_NOTE_CHANGED) != 0)
-+ gam_kqueue_file_monitor_emit_event(fmon, GAMIN_EVENT_CHANGED, 0);
-+
-+ /* emitting the following events will free the fmon */
-+ if ((event->fflags & VN_NOTE_DELETED) != 0)
-+ gam_kqueue_file_monitor_emit_event(fmon, GAMIN_EVENT_DELETED, MONITOR_ISNOTDIR | FILE_MONITOR_POSSIBLY_RECREATED);
-+ else if ((event->fflags & NOTE_RENAME) != 0)
-+ gam_kqueue_file_monitor_emit_event(fmon, GAMIN_EVENT_MOVED, MONITOR_ISNOTDIR | FILE_MONITOR_POSSIBLY_RECREATED);
-+}
-
-- GAM_DEBUG(DEBUG_INFO, "gam_kqueue_consume_subscriptions()\n");
-+/*** kevent/poll callbacks ***************************************************/
-+
-+static gboolean
-+gam_kqueue_kevent_cb (GIOChannel *source,
-+ GIOCondition condition,
-+ gpointer user_data)
-+{
-+ int nevents;
-+ struct kevent ev[1];
-+ struct timespec timeout = { 0, 0 };
-+ int i;
-+
-+ nevents = kevent(kq, NULL, 0, ev, G_N_ELEMENTS(ev), &timeout);
-+ if (nevents < 0)
-+ {
-+ GAM_DEBUG(DEBUG_INFO, "kevent() failure: %s\n", g_strerror(errno));
-+ return TRUE; /* keep source */
-+ }
-+
-+ for (i = 0; i < nevents; i++)
-+ MONITOR(ev[i].udata)->handle_kevent(ev[i].udata, &ev[i]);
-+
-+ return TRUE; /* keep source */
-+}
-+
-+static void
-+gam_kqueue_missing_smon_poll (SubMonitor *smon)
-+{
-+ struct stat sb;
-
-- have_consume_idler = FALSE;
-- return FALSE;
-+ if (lstat(MONITOR(smon)->pathname, &sb) >= 0)
-+ {
-+ gam_kqueue_poller_remove_sub_monitor(&missing_smon_poller, smon);
-+ gam_kqueue_sub_monitor_enable_notification(smon, SUB_MONITOR_WAS_MISSING | ((sb.st_mode & S_IFDIR) != 0 ? MONITOR_ISDIR : MONITOR_ISNOTDIR));
-+ }
- }
-
- static void
--gam_kqueue_consume_subscriptions(void)
-+gam_kqueue_unsupported_smon_poll (SubMonitor *smon)
- {
-- GSource *source;
-+ Monitor *mon = MONITOR(smon);
-+ MiniStat sb;
-+ GaminEventType event;
-+
-+ if (++mon->poll_count == CFG_UNSUPPORTED_SMON_KQUEUE_RETRY_FREQUENCY)
-+ {
-+ mon->poll_count = 0;
-+ if (gam_kqueue_monitor_enable_kqueue(mon))
-+ gam_kqueue_poller_remove_sub_monitor(&missing_smon_poller, smon);
-+ }
-+
-+ gam_kqueue_mini_lstat(mon->pathname, &sb);
-+
-+ if (! sb.exists && mon->sb.exists)
-+ event = GAMIN_EVENT_DELETED;
-+ else if (gam_kqueue_differs(&sb, &mon->sb))
-+ event = GAMIN_EVENT_CHANGED;
-+ else
-+ return;
-+
-+ memcpy(&mon->sb, &sb, sizeof(sb));
-+ gam_kqueue_sub_monitor_emit_event(smon, event, (sb.mode & S_IFDIR) != 0 ? MONITOR_ISDIR : MONITOR_ISNOTDIR);
-+}
-
-- if (have_consume_idler)
-- return;
-+static void
-+gam_kqueue_unsupported_fmon_poll_foreach_cb (FileMonitor *fmon,
-+ gpointer unused,
-+ gpointer user_data)
-+{
-+ Monitor *mon = MONITOR(fmon);
-+ MiniStat sb;
-+ GaminEventType event;
-+
-+ if (++mon->poll_count == CFG_UNSUPPORTED_FMON_KQUEUE_RETRY_FREQUENCY)
-+ {
-+ mon->poll_count = 0;
-+ if (gam_kqueue_monitor_enable_kqueue(mon))
-+ gam_kqueue_hash_table_remove(fmon->smon->unsupported_fmons, fmon);
-+ }
-
-- have_consume_idler = TRUE;
-+ gam_kqueue_mini_lstat(mon->pathname, &sb);
-
-- source = g_idle_source_new ();
-- g_source_set_callback (source, gam_kqueue_consume_subscriptions_real, NULL, NULL);
-+ if (! sb.exists && mon->sb.exists)
-+ event = GAMIN_EVENT_DELETED;
-+ else if (gam_kqueue_differs(&sb, &mon->sb))
-+ event = GAMIN_EVENT_CHANGED;
-+ else
-+ return;
-
-- g_source_attach (source, NULL);
-+ memcpy(&mon->sb, &sb, sizeof(sb));
-+ gam_kqueue_file_monitor_emit_event(fmon, event, (sb.mode & S_IFDIR) != 0 ? MONITOR_ISDIR : MONITOR_ISNOTDIR);
- }
-
--/**
-- * @defgroup kqueue kqueue backend
-- * @ingroup Backends
-- * @brief kqueue backend API
-- *
-- * Since 4.1, FreeBSD kernels have included the kernel event notification
-- * machanism (kqueue). This backend uses kqueue to know when
-- * files are changed/created/deleted.
-- *
-- * @{
-- */
-+static void
-+gam_kqueue_unsupported_fmon_poll (SubMonitor *smon)
-+{
-+ gam_kqueue_hash_table_foreach(smon->unsupported_fmons, (GHFunc) gam_kqueue_unsupported_fmon_poll_foreach_cb, NULL);
-+}
-
-+/*** Gamin backend implementation ********************************************/
-
- /**
- * Initializes the kqueue system. This must be called before
-@@ -526,28 +1118,58 @@
- * @returns TRUE if initialization succeeded, FALSE otherwise
- */
- gboolean
--gam_kqueue_init(void)
-+gam_kqueue_init (void)
- {
-- kq = kqueue();
-- if (kq == -1) {
-- GAM_DEBUG(DEBUG_INFO, "Could not initialize a new kqueue\n");
-- return FALSE;
-+ GIOChannel *channel;
-+ unsigned int maxfiles;
-+ unsigned int maxfilesperproc;
-+
-+ kq = kqueue();
-+ if (kq < 0)
-+ {
-+ gam_error(DEBUG_INFO, "kqueue initialization failure: %s\n", g_strerror(errno));
-+ return FALSE;
- }
-
-- g_timeout_add(1000, gam_kqueue_exist_check, NULL);
-- g_timeout_add(1000, gam_kqueue_event_handler, NULL);
-+ if (! gam_kqueue_get_uint_sysctl("kern.maxfiles", &maxfiles))
-+ return FALSE;
-+ if (! gam_kqueue_get_uint_sysctl("kern.maxfilesperproc", &maxfilesperproc))
-+ return FALSE;
-+
-+ /*
-+ * We make sure to:
-+ * - never paralyze the system (CFG_GLOBAL_FILE_RESERVE_RATIO)
-+ * - never paralyze our own process (CFG_SELF_FILE_RESERVE)
-+ */
-+
-+ maxfiles *= CFG_GLOBAL_FILE_RESERVE_RATIO;
-+ maxfilesperproc = maxfilesperproc > CFG_SELF_FILE_RESERVE
-+ ? maxfilesperproc - CFG_SELF_FILE_RESERVE
-+ : 0;
-+
-+ max_open_files = MIN(maxfiles, maxfilesperproc);
-+
-+ dir_hash = g_hash_table_new(g_str_hash, g_str_equal);
-+ file_hash = g_hash_table_new(g_str_hash, g_str_equal);
-+
-+ gam_kqueue_poller_init(&missing_smon_poller,
-+ gam_kqueue_missing_smon_poll,
-+ CFG_MISSING_SMON_POLL_INTERVAL);
-+ gam_kqueue_poller_init(&unsupported_smon_poller,
-+ gam_kqueue_unsupported_smon_poll,
-+ CFG_UNSUPPORTED_SMON_POLL_INTERVAL);
-+ gam_kqueue_poller_init(&unsupported_fmon_poller,
-+ gam_kqueue_unsupported_fmon_poll,
-+ CFG_UNSUPPORTED_FMON_POLL_INTERVAL);
-+
-+ channel = g_io_channel_unix_new(kq);
-+ g_io_add_watch(channel, G_IO_IN, gam_kqueue_kevent_cb, NULL);
-+
-+ gam_backend_add_subscription = gam_kqueue_add_subscription;
-+ gam_backend_remove_subscription = gam_kqueue_remove_subscription;
-+ gam_backend_remove_all_for = gam_kqueue_remove_all_for;
-
-- dir_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
-- file_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
-- fd_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
--
-- GAM_DEBUG(DEBUG_INFO, "kqueue initialized\n");
--
-- gam_backend_add_subscription = gam_kqueue_add_subscription;
-- gam_backend_remove_subscription = gam_kqueue_remove_subscription;
-- gam_backend_remove_all_for = gam_kqueue_remove_all_for;
--
-- return TRUE;
-+ return TRUE;
- }
-
- /**
-@@ -557,18 +1179,31 @@
- * @returns TRUE if adding the subscription succeeded, FALSE otherwise
- */
- gboolean
--gam_kqueue_add_subscription(GamSubscription * sub)
-+gam_kqueue_add_subscription (GamSubscription *sub)
- {
-- gam_listener_add_subscription(gam_subscription_get_listener(sub), sub);
--
-- G_LOCK(new_subs);
-- new_subs = g_list_prepend(new_subs, sub);
-- G_UNLOCK(new_subs);
-+ const char *path;
-+ GHashTable *hash;
-+ SubMonitor *smon;
-+
-+ gam_listener_add_subscription(gam_subscription_get_listener(sub), sub);
-+
-+ path = gam_subscription_get_path(sub);
-+ hash = gam_subscription_is_dir(sub) ? dir_hash : file_hash;
-+ smon = g_hash_table_lookup(hash, path);
-+
-+ if (smon)
-+ {
-+ smon->subs = g_list_append(smon->subs, sub);
-+ return TRUE;
-+ }
-+
-+ smon = gam_kqueue_sub_monitor_new(sub);
-+ smon->subs = g_list_append(smon->subs, sub);
-
-- GAM_DEBUG(DEBUG_INFO, "kqueue_add_sub\n");
-+ g_hash_table_insert(hash, MONITOR(smon)->pathname, smon);
-+ gam_kqueue_sub_monitor_enable_notification(smon, 0);
-
-- gam_kqueue_consume_subscriptions();
-- return TRUE;
-+ return TRUE;
- }
-
- /**
-@@ -578,28 +1213,28 @@
- * @returns TRUE if removing the subscription succeeded, FALSE otherwise
- */
- gboolean
--gam_kqueue_remove_subscription(GamSubscription * sub)
-+gam_kqueue_remove_subscription (GamSubscription *sub)
- {
-- G_LOCK(new_subs);
-- if (g_list_find(new_subs, sub)) {
-- GAM_DEBUG(DEBUG_INFO, "removed sub found on new_subs\n");
-- new_subs = g_list_remove_all (new_subs, sub);
-- G_UNLOCK(new_subs);
-- return TRUE;
-- }
-- G_UNLOCK(new_subs);
-+ GHashTable *hash;
-+ SubMonitor *smon;
-
-- gam_subscription_cancel (sub);
-- gam_listener_remove_subscription(gam_subscription_get_listener(sub), sub);
-+ hash = gam_subscription_is_dir(sub) ? dir_hash : file_hash;
-+ smon = g_hash_table_lookup(hash, gam_subscription_get_path(sub));
-
-- G_LOCK(removed_subs);
-- removed_subs = g_list_prepend (removed_subs, sub);
-- G_UNLOCK(removed_subs);
-+ if (! smon)
-+ return FALSE;
-
-- GAM_DEBUG(DEBUG_INFO, "kqueue_remove_sub\n");
-- gam_kqueue_consume_subscriptions();
-+ smon->subs = g_list_remove_all(smon->subs, sub);
-+ if (! smon->subs)
-+ {
-+ g_hash_table_remove(hash, MONITOR(smon)->pathname);
-+ gam_kqueue_sub_monitor_free(smon);
-+ }
-
-- return TRUE;
-+ gam_subscription_cancel(sub);
-+ gam_listener_remove_subscription(gam_subscription_get_listener(sub), sub);
-+
-+ return TRUE;
- }
-
- /**
-@@ -609,28 +1244,19 @@
- * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise
- */
- gboolean
--gam_kqueue_remove_all_for(GamListener * listener)
-+gam_kqueue_remove_all_for (GamListener *listener)
- {
-- GList *subs, *l = NULL;
--
-- subs = gam_listener_get_subscriptions (listener);
--
-- for (l = subs; l; l = l->next) {
-- GamSubscription *sub = l->data;
--
-- g_assert (sub != NULL);
--
-- gam_kqueue_remove_subscription (sub);
--
-- }
--
-- if (subs) {
-- g_list_free (subs);
-- gam_kqueue_consume_subscriptions();
-- return TRUE;
-- } else {
-- return FALSE;
-- }
-+ GList *subs;
-+ GList *l;
-+ gboolean success = TRUE;
-+
-+ subs = gam_listener_get_subscriptions(listener);
-+
-+ for (l = subs; l != NULL; l = l->next)
-+ if (! gam_kqueue_remove_subscription(l->data))
-+ success = FALSE;
-+
-+ g_list_free(subs);
-+
-+ return success;
- }
--
--/** @} */
diff --git a/devel/gamin/files/patch-server_gam_kqueue.h b/devel/gamin/files/patch-server_gam_kqueue.h
deleted file mode 100644
index 4a7d883da856..000000000000
--- a/devel/gamin/files/patch-server_gam_kqueue.h
+++ /dev/null
@@ -1,19 +0,0 @@
---- server/gam_kqueue.h.orig Thu May 19 20:35:00 2005
-+++ server/gam_kqueue.h Thu May 19 20:35:11 2005
-@@ -1,16 +1,8 @@
--
- #ifndef __MD_KQUEUE_H__
- #define __MD_KQUEUE_H__
-
- #include <glib.h>
--#include "gam_poll.h"
- #include "gam_subscription.h"
--
--#define VN_NOTE_MOST (NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | \
-- NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | \
-- NOTE_REVOKE)
--#define VN_NOTE_ALL VN_NOTE_MOST
--#define VN_NOTE_CHANGED (NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK)
-
- G_BEGIN_DECLS
-