/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim:set expandtab ts=4 shiftwidth=4: */ /* * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Lin Ma */ #include "config.h" #include #include "fen-data.h" #include "fen-helper.h" #include "fen-kernel.h" #ifdef GIO_COMPILATION #include "gfile.h" #include "gfilemonitor.h" #else #include "gam_event.h" #include "gam_server.h" #include "gam_protocol.h" #endif #ifdef GIO_COMPILATION #define FH_W if (fh_debug_enabled) g_warning static gboolean fh_debug_enabled = FALSE; #else #include "gam_error.h" #define FH_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__) #endif G_LOCK_EXTERN (fen_lock); static void default_emit_event_cb (fdata *f, int events); static void default_emit_once_event_cb (fdata *f, int events, gpointer sub); static int default_event_converter (int event); static void scan_children_init (node_t *f, gpointer sub) { GDir *dir; GError *err = NULL; node_op_t op = {NULL, NULL, _pre_del_cb, NULL}; fdata* pdata; FH_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f); pdata = _node_get_data (f); dir = g_dir_open (NODE_NAME(f), 0, &err); if (dir) { const char *basename; while ((basename = g_dir_read_name (dir))) { node_t *childf = NULL; fdata* data; GList *idx; childf = _children_find (f, basename); if (childf == NULL) { gchar *filename; filename = g_build_filename (NODE_NAME(f), basename, NULL); childf = _add_node (f, filename); g_assert (childf); g_free (filename); } if ((data = _node_get_data (childf)) == NULL) { data = _fdata_new (childf, FALSE); } if (is_monitoring (data)) { /* Ignored */ } else if (/* !_is_ported (data) && */ _port_add (&data->fobj, &data->len, data)) { /* Emit created to all other subs */ _fdata_emit_events (data, FN_EVENT_CREATED); } /* Emit created to the new sub */ #ifdef GIO_COMPILATION /* _fdata_emit_events_once (data, FN_EVENT_CREATED, sub); */ #else gam_server_emit_one_event (NODE_NAME(childf), gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1); #endif } g_dir_close (dir); } else { FH_W (err->message); g_error_free (err); } } /** * _fen_add * * Won't hold a ref, we have a timout callback to clean unused fdata. * If there is no value for a key, add it and return it; else return the old * one. */ void _fen_add (const gchar *filename, gpointer sub, gboolean is_mondir) { node_op_t op = {NULL, _add_missing_cb, _pre_del_cb, (gpointer)filename}; node_t* f; fdata* data; g_assert (filename); g_assert (sub); G_LOCK (fen_lock); f = _find_node_full (filename, &op); FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename); g_assert (f); data = _node_get_data (f); if (data == NULL) { data = _fdata_new (f, is_mondir); } if (is_mondir) { data->mon_dir_num ++; } /* Change to active */ #ifdef GIO_COMPILATION if (_port_add (&data->fobj, &data->len, data) || g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) { if (is_mondir) { scan_children_init (f, sub); } _fdata_sub_add (data, sub); } else { _fdata_sub_add (data, sub); _fdata_adjust_deleted (data); } #else if (_port_add (&data->fobj, &data->len, data) || g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) { gam_server_emit_one_event (FN_NAME(data), gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1); if (is_mondir) { scan_children_init (f, sub); } gam_server_emit_one_event (FN_NAME(data), gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1); _fdata_sub_add (data, sub); } else { _fdata_sub_add (data, sub); gam_server_emit_one_event (FN_NAME(data), gam_subscription_is_dir (sub), GAMIN_EVENT_DELETED, sub, 1); _fdata_adjust_deleted (data); gam_server_emit_one_event (FN_NAME(data), gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1); } #endif G_UNLOCK (fen_lock); } void _fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir) { node_op_t op = {NULL, _add_missing_cb, _pre_del_cb, (gpointer)filename}; node_t* f; fdata* data; g_assert (filename); g_assert (sub); G_LOCK (fen_lock); f = _find_node (filename); FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename); g_assert (f); data = _node_get_data (f); g_assert (data); if (is_mondir) { data->mon_dir_num --; } _fdata_sub_remove (data, sub); if (FN_IS_PASSIVE(data)) { #ifdef GIO_COMPILATION _pending_remove_node (f, &op); #else _remove_node (f, &op); #endif } G_UNLOCK (fen_lock); } static gboolean fen_init_once_func (gpointer data) { FH_W ("%s\n", __func__); if (!_node_class_init ()) { FH_W ("_node_class_init failed."); return FALSE; } if (!_fdata_class_init (default_emit_event_cb, default_emit_once_event_cb, default_event_converter)) { FH_W ("_fdata_class_init failed."); return FALSE; } return TRUE; } gboolean _fen_init () { #ifdef GIO_COMPILATION static GOnce fen_init_once = G_ONCE_INIT; g_once (&fen_init_once, (GThreadFunc)fen_init_once_func, NULL); return (gboolean)fen_init_once.retval; #else return fen_init_once_func (NULL); #endif } static void default_emit_once_event_cb (fdata *f, int events, gpointer sub) { #ifdef GIO_COMPILATION GFile* child; fen_sub* _sub = (fen_sub*)sub; child = g_file_new_for_path (FN_NAME(f)); g_file_monitor_emit_event (G_FILE_MONITOR (_sub->user_data), child, NULL, events); g_object_unref (child); #else gam_server_emit_one_event (FN_NAME(f), gam_subscription_is_dir (sub), events, sub, 1); #endif } static void default_emit_event_cb (fdata *f, int events) { GList* i; fdata* pdata; #ifdef GIO_COMPILATION GFile* child; child = g_file_new_for_path (FN_NAME(f)); for (i = f->subs; i; i = i->next) { fen_sub* sub = (fen_sub*)i->data; gboolean file_is_dir = sub->is_mondir; if ((events != G_FILE_MONITOR_EVENT_CHANGED && events != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) || !file_is_dir) { g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), child, NULL, events); } } if ((pdata = _get_parent_data (f)) != NULL) { for (i = pdata->subs; i; i = i->next) { fen_sub* sub = (fen_sub*)i->data; gboolean file_is_dir = sub->is_mondir; g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), child, NULL, events); } } g_object_unref (child); #else for (i = f->subs; i; i = i->next) { gboolean file_is_dir = gam_subscription_is_dir (i->data); if (events != GAMIN_EVENT_CHANGED || !file_is_dir) { gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1); } } if ((pdata = _get_parent_data (f)) != NULL) { for (i = pdata->subs; i; i = i->next) { gboolean file_is_dir = gam_subscription_is_dir (i->data); gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1); } } #endif } static int default_event_converter (int event) { #ifdef GIO_COMPILATION switch (event) { case FN_EVENT_CREATED: return G_FILE_MONITOR_EVENT_CREATED; case FILE_DELETE: case FILE_RENAME_FROM: return G_FILE_MONITOR_EVENT_DELETED; case UNMOUNTED: return G_FILE_MONITOR_EVENT_UNMOUNTED; case FILE_ATTRIB: return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED; case MOUNTEDOVER: case FILE_MODIFIED: case FILE_RENAME_TO: return G_FILE_MONITOR_EVENT_CHANGED; default: /* case FILE_ACCESS: */ g_assert_not_reached (); return -1; } #else switch (event) { case FN_EVENT_CREATED: return GAMIN_EVENT_CREATED; case FILE_DELETE: case FILE_RENAME_FROM: return GAMIN_EVENT_DELETED; case FILE_ATTRIB: case MOUNTEDOVER: case UNMOUNTED: case FILE_MODIFIED: case FILE_RENAME_TO: return GAMIN_EVENT_CHANGED; default: /* case FILE_ACCESS: */ g_assert_not_reached (); return -1; } #endif }