1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
4 * Copyright (C) 2008 Sun Microsystem.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Authors: Lin Ma <lin.ma@sun.com>
31 #define NODE_STAT(n) (((node_t*)(n))->stat)
39 #define FN_W if (fn_debug_enabled) g_warning
40 static gboolean fn_debug_enabled = FALSE;
42 G_LOCK_EXTERN (fen_lock);
43 #define PROCESS_DELETING_INTERVAL 900 /* in second */
45 static node_t* _head = NULL;
46 static GList *deleting_nodes = NULL;
47 static guint deleting_nodes_id = 0;
49 static node_t* node_new (node_t* parent, const gchar* basename);
50 static void node_delete (node_t* parent);
51 static gboolean remove_node_internal (node_t* node, node_op_t* op);
52 static void children_add (node_t *p, node_t *f);
53 static void children_remove (node_t *p, node_t *f);
54 static guint children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data);
55 static void children_foreach (node_t *f, GHFunc func, gpointer user_data);
56 static gboolean children_remove_cb (gpointer key,
61 _dnode_new (const gchar* filename, node_op_t* op)
66 if ((d = g_new (struct _dnode, 1)) != NULL) {
67 d->filename = g_strdup (filename);
68 d->op = g_memdup (op, sizeof (node_op_t));
70 g_get_current_time (&d->tv);
71 g_time_val_add (&d->tv, PROCESS_DELETING_INTERVAL);
77 _dnode_free (struct _dnode* d)
86 g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
88 if (val1->tv_sec < val2->tv_sec)
91 if (val1->tv_sec > val2->tv_sec)
94 /* val1->tv_sec == val2->tv_sec */
95 if (val1->tv_usec < val2->tv_usec)
102 scan_deleting_nodes (gpointer data)
107 GList* deleted_list = NULL;
111 g_get_current_time (&tv_now);
113 if (G_TRYLOCK (fen_lock)) {
114 for (i = deleting_nodes; i; i = i->next) {
115 d = (struct _dnode*)i->data;
116 /* Time to free, try only once */
117 if (g_timeval_lt (&d->tv, &tv_now)) {
118 if ((node = find_node (d->filename)) != NULL) {
119 remove_node_internal (node, d->op);
122 deleted_list = g_list_prepend (deleted_list, i);
126 for (i = deleted_list; i; i = i->next) {
127 deleting_nodes = g_list_remove_link (deleting_nodes,
129 g_list_free_1 ((GList *)i->data);
131 g_list_free (deleted_list);
133 if (deleting_nodes == NULL) {
134 deleting_nodes_id = 0;
143 node_get_data (node_t* node)
146 return node->user_data;
150 node_set_data (node_t* node, gpointer user_data)
152 gpointer data = node->user_data;
154 node->user_data = user_data;
159 travel_nodes (node_t* node, node_op_t* op)
166 op->hit (node, op->user_data);
169 children = g_hash_table_get_values (node->children);
171 for (i = children; i; i = i->next) {
172 travel_nodes (i->data, op);
174 g_list_free (children);
179 find_node_internal (node_t* node, const gchar* filename, node_op_t* op)
187 g_assert (filename && filename[0] == '/');
191 str = g_strdup (filename + strlen (NODE_NAME(parent)));
193 if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
195 FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
196 child = children_find (parent, token);
200 if (op && op->add_missing) {
201 child = op->add_missing (parent, op->user_data);
206 } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
209 g_assert (parent == _head);
213 if (token == NULL && child) {
216 op->hit (child, op->user_data);
224 find_node (const gchar *filename)
226 return find_node_internal (_head, filename, NULL);
230 find_node_full (const gchar* filename, node_op_t* op)
232 return find_node_internal (_head, filename, op);
236 add_node (node_t* parent, const gchar* filename)
241 node_t* child = NULL;
244 g_assert (filename && filename[0] == '/');
246 if (parent == NULL) {
250 str = g_strdup (filename + strlen (NODE_NAME(parent)));
252 if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
254 FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
255 child = node_new (parent, token);
257 children_add (parent, child);
262 } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
276 remove_children (node_t* node, node_op_t* op)
278 FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
279 if (children_num (node) > 0) {
280 children_foreach_remove (node, children_remove_cb,
283 if (children_num (node) == 0) {
290 remove_node_internal (node_t* node, node_op_t* op)
292 node_t* parent = NULL;
294 * If the parent is passive and doesn't have children, delete it.
295 * NOTE node_delete_deep is a depth first delete recursively.
296 * Top node is deleted in node_cancel_sub
299 g_assert (op && op->pre_del);
301 if (remove_children (node, op)) {
302 if (node->user_data) {
303 if (!op->pre_del (node, op->user_data)) {
307 parent = node->parent;
308 children_remove (parent, node);
310 if (children_num (parent) == 0) {
311 remove_node_internal (parent, op);
321 pending_remove_node (node_t* node, node_op_t* op)
326 for (l = deleting_nodes; l; l=l->next) {
327 d = (struct _dnode*) l->data;
328 if (g_ascii_strcasecmp (d->filename, NODE_NAME(node)) == 0) {
333 d = _dnode_new (NODE_NAME(node), op);
335 deleting_nodes = g_list_prepend (deleting_nodes, d);
336 if (deleting_nodes_id == 0) {
337 deleting_nodes_id = g_timeout_add_seconds (PROCESS_DELETING_INTERVAL,
340 g_assert (deleting_nodes_id > 0);
345 remove_node (node_t* node, node_op_t* op)
347 remove_node_internal (node, op);
351 node_new (node_t* parent, const gchar* basename)
355 g_assert (basename && basename[0]);
356 if ((f = g_new0 (node_t, 1)) != NULL) {
358 f->basename = g_strdup (basename);
359 f->filename = g_build_filename (G_DIR_SEPARATOR_S,
360 NODE_NAME(parent), basename, NULL);
362 f->basename = g_strdup (basename);
363 f->filename = g_strdup (basename);
365 f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
366 NULL, (GDestroyNotify)node_delete);
367 FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
373 node_delete (node_t *f)
375 FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
376 g_assert (g_hash_table_size (f->children) == 0);
377 g_assert (f->user_data == NULL);
379 g_hash_table_unref (f->children);
380 g_free (f->basename);
381 g_free (f->filename);
386 children_add (node_t *p, node_t *f)
388 FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
389 g_hash_table_insert (p->children, f->basename, f);
394 children_remove (node_t *p, node_t *f)
396 FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
397 g_hash_table_steal (p->children, f->basename);
402 children_num (node_t *f)
404 return g_hash_table_size (f->children);
408 children_find (node_t *f, const gchar *basename)
410 return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
414 * depth first delete recursively
417 children_remove_cb (gpointer key,
421 node_t* f = (node_t*)value;
422 node_op_t* op = (node_op_t*) user_data;
424 g_assert (f->parent);
426 FN_W ("%s [p] %8s [c] %8s\n", __func__, f->parent->basename, f->basename);
427 if (remove_children (f, op)) {
428 if (f->user_data != NULL) {
429 return op->pre_del (f, op->user_data);
437 children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data)
441 return g_hash_table_foreach_remove (f->children, func, user_data);
445 children_foreach (node_t *f, GHFunc func, gpointer user_data)
449 g_hash_table_foreach (f->children, func, user_data);
455 FN_W ("%s\n", __func__);
457 _head = node_new (NULL, G_DIR_SEPARATOR_S);
459 return _head != NULL;