doc: Clarify documentation regarding g_file_replace and etags
[platform/upstream/glib.git] / gio / fam / fam-helper.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Authors: Alexander Larsson <alexl@redhat.com>
19  *          John McCutchan <john@johnmccutchan.com> 
20  *          Sebastian Dröge <slomo@circular-chaos.org>
21  */
22
23 #include "config.h"
24 #include <fam.h>
25 #include <gio/gfilemonitor.h>
26 #include <gio/gfile.h>
27
28 #include "fam-helper.h"
29
30 static FAMConnection* fam_connection = NULL;
31 static gint fam_watch_id = 0;
32 G_LOCK_DEFINE_STATIC(fam_connection);
33
34 struct _fam_sub
35 {
36   gchar *pathname;
37   gboolean directory;
38   gpointer user_data;
39   gboolean cancelled;
40   FAMRequest request;
41 };
42
43 /* This uses int as the argument type because the
44    real type differs between implementations:
45    gamin has "typedef enum FAMCodes {....} FAMCodes;"
46    fam has "enum FAMCodes { ... }".
47 */
48 static GFileMonitorEvent  
49 fam_event_to_file_monitor_event (int code)
50 {
51   switch (code)
52     {
53     case FAMChanged:
54       return G_FILE_MONITOR_EVENT_CHANGED;
55       break;
56     case FAMDeleted:
57       return G_FILE_MONITOR_EVENT_DELETED;
58       break;
59     case FAMCreated:
60       return G_FILE_MONITOR_EVENT_CREATED;
61       break;
62     default:
63       return -1;
64       break;
65     }
66 }
67
68 static gboolean
69 fam_do_iter_unlocked (void)
70 {
71   while (fam_connection != NULL && FAMPending (fam_connection)) 
72     {
73       FAMEvent ev;
74       fam_sub* sub = NULL;
75       gboolean cancelled;
76     
77       if (FAMNextEvent (fam_connection, &ev) != 1) 
78         {
79           FAMClose (fam_connection);
80           g_free (fam_connection);
81           g_source_remove (fam_watch_id);
82           fam_watch_id = 0;
83           fam_connection = NULL;
84           return FALSE;
85         }
86     
87       sub = (fam_sub*)ev.userdata;
88       cancelled = sub->cancelled;
89       if (ev.code == FAMAcknowledge && cancelled)
90         {
91           _fam_sub_free (sub);
92           continue;
93         }
94     
95       if (cancelled)
96         continue;
97     
98       if (sub->directory)
99         {
100           GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
101           GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
102           gchar* path = NULL;
103           GFile *child, *parent;
104         
105           /* unsupported event */
106           if (eflags == -1)
107             continue;
108         
109           if (ev.filename[0] == '/')
110             path = g_strdup (ev.filename);
111           else
112             path = g_strdup_printf ("%s/%s", sub->pathname, ev.filename);
113
114           child = g_file_new_for_path (path);
115           parent = g_file_get_parent (child);
116           g_file_monitor_emit_event (monitor, child, NULL, eflags);
117           g_free (path);
118           g_object_unref (child);
119           g_object_unref (parent);
120         } 
121       else 
122         {
123           GFile *child;
124           GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
125           GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
126           gchar* path = NULL;
127       
128           if (eflags == -1)
129             continue;
130           path = g_strdup (ev.filename);
131           child = g_file_new_for_path (path);
132           g_file_monitor_emit_event (monitor, child, NULL, eflags);
133           g_free (path);
134           g_object_unref (child);
135         }
136     }
137   
138   return TRUE;
139 }
140
141 static gboolean
142 fam_callback (GIOChannel   *source,
143               GIOCondition  condition,
144               gpointer      data)
145 {
146   gboolean res;
147   G_LOCK (fam_connection);
148   
149   res = fam_do_iter_unlocked ();
150   
151   G_UNLOCK (fam_connection);
152   return res;
153 }
154
155 gboolean
156 _fam_sub_startup (void)
157 {
158   GIOChannel *ioc;
159   
160   G_LOCK (fam_connection);
161   
162   if (fam_connection == NULL) 
163     {
164       fam_connection = g_new0 (FAMConnection, 1);
165       if (FAMOpen2 (fam_connection, "gvfs user") != 0) 
166         {
167           g_warning ("FAMOpen failed, FAMErrno=%d\n", FAMErrno);
168           g_free (fam_connection);
169           fam_connection = NULL;
170           G_UNLOCK (fam_connection);
171           return FALSE;
172         }
173 #ifdef HAVE_FAM_NO_EXISTS
174     /* This is a gamin extension that avoids sending all the Exists event for dir monitors */
175       FAMNoExists (fam_connection);
176 #endif
177       ioc = g_io_channel_unix_new (FAMCONNECTION_GETFD(fam_connection));
178       fam_watch_id = g_io_add_watch (ioc,
179                                      G_IO_IN | G_IO_HUP | G_IO_ERR,
180                                      fam_callback, fam_connection);
181       g_io_channel_unref (ioc);
182     }
183   
184   G_UNLOCK (fam_connection);
185   
186   return TRUE;
187 }
188
189 void
190 _fam_sub_shutdown (void)
191 {
192   G_LOCK (fam_connection);
193
194   if (fam_connection != NULL) 
195     {
196       FAMClose (fam_connection);
197       g_free (fam_connection);
198       g_source_remove (fam_watch_id);
199       fam_watch_id = 0;
200       fam_connection = NULL;
201     }
202
203   G_UNLOCK (fam_connection);
204 }
205
206 fam_sub*
207 _fam_sub_add (const gchar *pathname,
208               gboolean     directory,
209               gpointer     user_data)
210 {
211   fam_sub *sub;
212
213   if (!_fam_sub_startup ())
214     return NULL;
215   
216   G_LOCK (fam_connection);
217   /* We need to queue up incoming messages to avoid blocking on write
218    *  if there are many monitors being canceled */
219   fam_do_iter_unlocked ();
220   
221   if (fam_connection == NULL) 
222     {
223       G_UNLOCK (fam_connection);
224       return NULL;
225     }
226
227   sub = g_new0 (fam_sub, 1);
228   sub->pathname = g_strdup (pathname);
229   sub->directory = directory;
230   sub->user_data = user_data;
231   
232   if (directory)
233     FAMMonitorDirectory (fam_connection, pathname, &sub->request, sub);
234   else
235     FAMMonitorFile (fam_connection, pathname, &sub->request, sub);
236   
237   G_UNLOCK (fam_connection);
238
239   return sub;
240 }
241
242 gboolean
243 _fam_sub_cancel (fam_sub* sub)
244 {
245   if (sub->cancelled)
246     return TRUE;
247   
248   sub->cancelled = TRUE;
249   
250   G_LOCK (fam_connection);
251   /* We need to queue up incoming messages to avoid blocking on write
252    *  if there are many monitors being canceled */
253   fam_do_iter_unlocked ();
254   
255   if (fam_connection == NULL) 
256     {
257       G_UNLOCK (fam_connection);
258       return FALSE;
259     }
260   
261   FAMCancelMonitor (fam_connection, &sub->request);
262   
263   G_UNLOCK (fam_connection);
264   
265   return TRUE;
266 }
267
268 void
269 _fam_sub_free (fam_sub* sub)
270 {
271   g_free (sub->pathname);
272   g_free (sub);
273 }
274