Added. Added. Added. Added. Added. Added. Added. Added. Added. Added.
[platform/upstream/glib.git] / gio / fen / fen-node.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
3 /* 
4  * Copyright (C) 2008 Sun Microsystem.
5  *
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.
10  *
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.
15  *
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.
20  *
21  * Authors: Lin Ma <lin.ma@sun.com>
22  */
23
24 #include "config.h"
25 #include <errno.h>
26 #include <strings.h>
27 #include <glib.h>
28 #include "fen-node.h"
29 #include "fen-dump.h"
30
31 #define NODE_STAT(n)    (((node_t*)(n))->stat)
32
33 struct _dnode {
34     gchar* filename;
35     node_op_t* op;
36     GTimeVal tv;
37 };
38
39 #define FN_W if (fn_debug_enabled) g_warning
40 static gboolean fn_debug_enabled = FALSE;
41
42 G_LOCK_EXTERN (fen_lock);
43 #define PROCESS_DELETING_INTERVAL       900 /* in second */
44
45 static node_t* _head = NULL;
46 static GList *deleting_nodes = NULL;
47 static guint deleting_nodes_id = 0;
48
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,
57   gpointer value,
58   gpointer user_data);
59
60 static struct _dnode*
61 _dnode_new (const gchar* filename, node_op_t* op)
62 {
63     struct _dnode* d;
64
65     g_assert (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));
69         g_assert (d->op);
70         g_get_current_time (&d->tv);
71         g_time_val_add (&d->tv, PROCESS_DELETING_INTERVAL);
72     }
73     return d;
74 }
75
76 static void
77 _dnode_free (struct _dnode* d)
78 {
79     g_assert (d);
80     g_free (d->filename);
81     g_free (d->op);
82     g_free (d);
83 }
84
85 static gboolean
86 g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
87 {
88     if (val1->tv_sec < val2->tv_sec)
89         return TRUE;
90   
91     if (val1->tv_sec > val2->tv_sec)
92         return FALSE;
93   
94     /* val1->tv_sec == val2->tv_sec */
95     if (val1->tv_usec < val2->tv_usec)
96         return TRUE;
97   
98     return FALSE;
99 }
100
101 static gboolean
102 scan_deleting_nodes (gpointer data)
103 {
104     struct _dnode* d;
105     GTimeVal tv_now;
106     GList* i;
107     GList* deleted_list = NULL;
108     gboolean ret = TRUE;
109     node_t* node;
110
111     g_get_current_time (&tv_now);
112
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);
120                 }
121                 _dnode_free (d);
122                 deleted_list = g_list_prepend (deleted_list, i);
123             }
124         }
125
126         for (i = deleted_list; i; i = i->next) {
127             deleting_nodes = g_list_remove_link (deleting_nodes,
128               (GList *)i->data);
129             g_list_free_1 ((GList *)i->data);
130         }
131         g_list_free (deleted_list);
132
133         if (deleting_nodes == NULL) {
134             deleting_nodes_id = 0;
135             ret = FALSE;
136         }
137         G_UNLOCK (fen_lock);
138     }
139     return ret;
140 }
141
142 gpointer
143 node_get_data (node_t* node)
144 {
145     g_assert (node);
146     return node->user_data;
147 }
148
149 gpointer
150 node_set_data (node_t* node, gpointer user_data)
151 {
152     gpointer data = node->user_data;
153     g_assert (node);
154     node->user_data = user_data;
155     return data;
156 }
157
158 void
159 travel_nodes (node_t* node, node_op_t* op)
160 {
161     GList* children;
162     GList* i;
163
164     if (node) {
165         if (op && op->hit) {
166             op->hit (node, op->user_data);
167         }
168     }
169     children = g_hash_table_get_values (node->children);
170     if (children) {
171         for (i = children; i; i = i->next) {
172             travel_nodes (i->data, op);
173         }
174         g_list_free (children);
175     }
176 }
177
178 static node_t*
179 find_node_internal (node_t* node, const gchar* filename, node_op_t* op)
180 {
181     gchar* str;
182     gchar* token;
183     gchar* lasts;
184     node_t* parent;
185     node_t* child;
186     
187     g_assert (filename && filename[0] == '/');
188     g_assert (node);
189     
190     parent = node;
191     str = g_strdup (filename + strlen (NODE_NAME(parent)));
192     
193     if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
194         do {
195             FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
196             child = children_find (parent, token);
197             if (child) {
198                 parent = child;
199             } else {
200                 if (op && op->add_missing) {
201                     child = op->add_missing (parent, op->user_data);
202                     goto L_hit;
203                 }
204                 break;
205             }
206         } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
207     } else {
208         /* It's the head */
209         g_assert (parent == _head);
210         child = _head;
211     }
212     
213     if (token == NULL && child) {
214     L_hit:
215         if (op && op->hit) {
216             op->hit (child, op->user_data);
217         }
218     }
219     g_free (str);
220     return child;
221 }
222
223 node_t*
224 find_node (const gchar *filename)
225 {
226     return find_node_internal (_head, filename, NULL);
227 }
228
229 node_t*
230 find_node_full (const gchar* filename, node_op_t* op)
231 {
232     return find_node_internal (_head, filename, op);
233 }
234
235 node_t*
236 add_node (node_t* parent, const gchar* filename)
237 {
238     gchar* str;
239     gchar* token;
240     gchar* lasts;
241     node_t* child = NULL;
242
243     g_assert (_head);
244     g_assert (filename && filename[0] == '/');
245
246     if (parent == NULL) {
247         parent = _head;
248     }
249     
250     str = g_strdup (filename + strlen (NODE_NAME(parent)));
251     
252     if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
253         do {
254             FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
255             child = node_new (parent, token);
256             if (child) {
257                 children_add (parent, child);
258                 parent = child;
259             } else {
260                 break;
261             }
262         } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
263     }
264     g_free (str);
265     if (token == NULL) {
266         return child;
267     } else {
268         return NULL;
269     }
270 }
271
272 /**
273  * delete recursively
274  */
275 static gboolean
276 remove_children (node_t* node, node_op_t* op)
277 {
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,
281           (gpointer)op);
282     }
283     if (children_num (node) == 0) {
284         return TRUE;
285     }
286     return FALSE;
287 }
288
289 static gboolean
290 remove_node_internal (node_t* node, node_op_t* op)
291 {
292     node_t* parent = NULL;
293     /*
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
297      */
298     g_assert (node);
299     g_assert (op && op->pre_del);
300     if (node != _head) {
301         if (remove_children (node, op)) {
302             if (node->user_data) {
303                 if (!op->pre_del (node, op->user_data)) {
304                     return FALSE;
305                 }
306             }
307             parent = node->parent;
308             children_remove (parent, node);
309             node_delete (node);
310             if (children_num (parent) == 0) {
311                 remove_node_internal (parent, op);
312             }
313             return TRUE;
314         }
315         return FALSE;
316     }
317     return TRUE;
318 }
319
320 void
321 pending_remove_node (node_t* node, node_op_t* op)
322 {
323     struct _dnode* d;
324     GList* l;
325     
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) {
329             return;
330         }
331     }
332     
333     d = _dnode_new (NODE_NAME(node), op);
334     g_assert (d);
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,
338           scan_deleting_nodes,
339           NULL);
340         g_assert (deleting_nodes_id > 0);
341     }
342 }
343
344 void
345 remove_node (node_t* node, node_op_t* op)
346 {
347     remove_node_internal (node, op);
348 }
349
350 static node_t*
351 node_new (node_t* parent, const gchar* basename)
352 {
353         node_t *f = NULL;
354
355     g_assert (basename && basename[0]);
356     if ((f = g_new0 (node_t, 1)) != NULL) {
357         if (parent) {
358             f->basename = g_strdup (basename);
359             f->filename = g_build_filename (G_DIR_SEPARATOR_S,
360               NODE_NAME(parent), basename, NULL);
361         } else {
362             f->basename = g_strdup (basename);
363             f->filename = g_strdup (basename);
364         }
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));
368     }
369         return f;
370 }
371
372 static void
373 node_delete (node_t *f)
374 {
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);
378
379     g_hash_table_unref (f->children);
380     g_free (f->basename);
381     g_free (f->filename);
382     g_free (f);
383 }
384
385 static void
386 children_add (node_t *p, node_t *f)
387 {
388     FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
389     g_hash_table_insert (p->children, f->basename, f);
390     f->parent = p;
391 }
392
393 static void
394 children_remove (node_t *p, node_t *f)
395 {
396     FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
397     g_hash_table_steal (p->children, f->basename);
398     f->parent = NULL;
399 }
400
401 guint
402 children_num (node_t *f)
403 {
404     return g_hash_table_size (f->children);
405 }
406
407 node_t *
408 children_find (node_t *f, const gchar *basename)
409 {
410     return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
411 }
412
413 /**
414  * depth first delete recursively
415  */
416 static gboolean
417 children_remove_cb (gpointer key,
418   gpointer value,
419   gpointer user_data)
420 {
421     node_t* f = (node_t*)value;
422     node_op_t* op = (node_op_t*) user_data;
423     
424     g_assert (f->parent);
425
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);
430         }
431         return TRUE;
432     }
433     return FALSE;
434 }
435
436 static guint
437 children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data)
438 {
439     g_assert (f);
440     
441     return g_hash_table_foreach_remove (f->children, func, user_data);
442 }
443
444 static void
445 children_foreach (node_t *f, GHFunc func, gpointer user_data)
446 {
447     g_assert (f);
448     
449     g_hash_table_foreach (f->children, func, user_data);
450 }
451
452 gboolean
453 node_class_init ()
454 {
455     FN_W ("%s\n", __func__);
456     if (_head == NULL) {
457         _head = node_new (NULL, G_DIR_SEPARATOR_S);
458     }
459     return _head != NULL;
460 }