]> code.delx.au - gnu-emacs/blob - src/gfilenotify.c
Implement gfile-valid-p
[gnu-emacs] / src / gfilenotify.c
1 /* Filesystem notifications support with glib API.
2 Copyright (C) 2013-2015 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Emacs 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
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
19 #include <config.h>
20
21 #ifdef HAVE_GFILENOTIFY
22 #include <stdio.h>
23 #include <gio/gio.h>
24 #include "lisp.h"
25 #include "coding.h"
26 #include "frame.h"
27 #include "termhooks.h"
28 #include "keyboard.h"
29 #include "process.h"
30
31 \f
32 /* This is a list, elements are triples (DESCRIPTOR FILE CALLBACK) */
33 static Lisp_Object watch_list;
34
35 /* This is the callback function for arriving signals from
36 g_file_monitor. It shall create a Lisp event, and put it into
37 Emacs input queue. */
38 static gboolean
39 dir_monitor_callback (GFileMonitor *monitor,
40 GFile *file,
41 GFile *other_file,
42 GFileMonitorEvent event_type,
43 gpointer user_data)
44 {
45 Lisp_Object symbol, monitor_object, watch_object;
46 char *name = g_file_get_parse_name (file);
47 char *oname = other_file ? g_file_get_parse_name (other_file) : NULL;
48
49 /* Determine event symbol. */
50 switch (event_type)
51 {
52 case G_FILE_MONITOR_EVENT_CHANGED:
53 symbol = Qchanged;
54 break;
55 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
56 symbol = Qchanges_done_hint;
57 break;
58 case G_FILE_MONITOR_EVENT_DELETED:
59 symbol = Qdeleted;
60 break;
61 case G_FILE_MONITOR_EVENT_CREATED:
62 symbol = Qcreated;
63 break;
64 case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
65 symbol = Qattribute_changed;
66 break;
67 case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
68 symbol = Qpre_unmount;
69 break;
70 case G_FILE_MONITOR_EVENT_UNMOUNTED:
71 symbol = Qunmounted;
72 break;
73 case G_FILE_MONITOR_EVENT_MOVED:
74 symbol = Qmoved;
75 break;
76 default:
77 goto cleanup;
78 }
79
80 /* Determine callback function. */
81 monitor_object = make_pointer_integer (monitor);
82 eassert (INTEGERP (monitor_object));
83 watch_object = assq_no_quit (monitor_object, watch_list);
84
85 if (CONSP (watch_object))
86 {
87 /* Construct an event. */
88 struct input_event event;
89 Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil;
90 EVENT_INIT (event);
91 event.kind = FILE_NOTIFY_EVENT;
92 event.frame_or_window = Qnil;
93 event.arg = list2 (Fcons (monitor_object,
94 Fcons (symbol,
95 Fcons (build_string (name),
96 otail))),
97 XCAR (XCDR (XCDR (watch_object))));
98
99 /* Store it into the input event queue. */
100 kbd_buffer_store_event (&event);
101
102 /* Cancel monitor if file or directory is deleted. */
103 if (((event_type == G_FILE_MONITOR_EVENT_DELETED) ||
104 (event_type == G_FILE_MONITOR_EVENT_MOVED)) &&
105 (strcmp (name, SSDATA (XCAR (XCDR (watch_object)))) == 0) &&
106 (!g_file_monitor_is_cancelled (monitor)))
107 g_file_monitor_cancel (monitor);
108 }
109
110 /* Cleanup. */
111 cleanup:
112 g_free (name);
113 g_free (oname);
114
115 return TRUE;
116 }
117
118 DEFUN ("gfile-add-watch", Fgfile_add_watch, Sgfile_add_watch, 3, 3, 0,
119 doc: /* Add a watch for filesystem events pertaining to FILE.
120
121 This arranges for filesystem events pertaining to FILE to be reported
122 to Emacs. Use `gfile-rm-watch' to cancel the watch.
123
124 Value is a descriptor for the added watch. If the file cannot be
125 watched for some reason, this function signals a `file-notify-error' error.
126
127 FLAGS is a list of conditions to set what will be watched for. It can
128 include the following symbols:
129
130 `watch-mounts' -- watch for mount events
131 `send-moved' -- pair `deleted' and `created' events caused by file
132 renames and send a single `renamed' event instead
133
134 When any event happens, Emacs will call the CALLBACK function passing
135 it a single argument EVENT, which is of the form
136
137 (DESCRIPTOR ACTION FILE [FILE1])
138
139 DESCRIPTOR is the same object as the one returned by this function.
140 ACTION is the description of the event. It could be any one of the
141 following:
142
143 `changed' -- FILE has changed
144 `changes-done-hint' -- a hint that this was probably the last change
145 in a set of changes
146 `deleted' -- FILE was deleted
147 `created' -- FILE was created
148 `attribute-changed' -- a FILE attribute was changed
149 `pre-unmount' -- the FILE location will soon be unmounted
150 `unmounted' -- the FILE location was unmounted
151 `moved' -- FILE was moved to FILE1
152
153 FILE is the name of the file whose event is being reported. FILE1
154 will be reported only in case of the `moved' event. */)
155 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
156 {
157 Lisp_Object watch_object;
158 GFile *gfile;
159 GFileMonitor *monitor;
160 GFileMonitorFlags gflags = G_FILE_MONITOR_NONE;
161 GError *gerror = NULL;
162
163 /* Check parameters. */
164 CHECK_STRING (file);
165 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
166 if (NILP (Ffile_exists_p (file)))
167 report_file_error ("File does not exist", file);
168
169 CHECK_LIST (flags);
170
171 if (!FUNCTIONP (callback))
172 wrong_type_argument (Qinvalid_function, callback);
173
174 /* Create GFile name. */
175 gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
176
177 /* Assemble flags. */
178 if (!NILP (Fmember (Qwatch_mounts, flags)))
179 gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
180 if (!NILP (Fmember (Qsend_moved, flags)))
181 gflags |= G_FILE_MONITOR_SEND_MOVED;
182
183 /* Enable watch. */
184 monitor = g_file_monitor (gfile, gflags, NULL, &gerror);
185 if (gerror)
186 {
187 char msg[1024];
188 strcpy (msg, gerror->message);
189 g_error_free (gerror);
190 xsignal1 (Qfile_notify_error, build_string (msg));
191 }
192 if (! monitor)
193 xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file);
194
195 Lisp_Object watch_descriptor = make_pointer_integer (monitor);
196
197 /* Check the dicey assumption that make_pointer_integer is safe. */
198 if (! INTEGERP (watch_descriptor))
199 {
200 g_object_unref (monitor);
201 xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"),
202 file);
203 }
204
205 g_signal_connect (monitor, "changed",
206 (GCallback) dir_monitor_callback, NULL);
207
208 /* Store watch object in watch list. */
209 watch_object = list3 (watch_descriptor, file, callback);
210 watch_list = Fcons (watch_object, watch_list);
211
212 return watch_descriptor;
213 }
214
215 DEFUN ("gfile-rm-watc", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
216 doc: /* Remove an existing WATCH-DESCRIPTOR.
217
218 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */)
219 (Lisp_Object watch_descriptor)
220 {
221 Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
222
223 if (! CONSP (watch_object))
224 xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
225 watch_descriptor);
226
227 eassert (INTEGERP (watch_descriptor));
228 GFileMonitor *monitor = XINTPTR (watch_descriptor);
229 if (!g_file_monitor_is_cancelled (monitor) &&
230 !g_file_monitor_cancel (monitor))
231 xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"),
232 watch_descriptor);
233
234 /* Remove watch descriptor from watch list. */
235 watch_list = Fdelq (watch_object, watch_list);
236
237 /* Cleanup. */
238 g_object_unref (monitor);
239
240 return Qt;
241 }
242
243 DEFUN ("gfile-valid-p", Fgfile_valid_p, Sgfile_valid_p, 1, 1, 0,
244 doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
245
246 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
247
248 A watch can become invalid if the file or directory it watches is
249 deleted, or if the watcher thread exits abnormally for any other
250 reason. Removing the watch by calling `gfile-rm-watch' also makes it
251 invalid. */)
252 (Lisp_Object watch_descriptor)
253 {
254 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
255 if (NILP (watch_object))
256 return Qnil;
257 else
258 {
259 GFileMonitor *monitor = XINTPTR (watch_descriptor);
260 return g_file_monitor_is_cancelled (monitor) ? Qnil : Qt;
261 }
262 }
263
264 \f
265 void
266 globals_of_gfilenotify (void)
267 {
268 #if ! GLIB_CHECK_VERSION (2, 36, 0)
269 g_type_init ();
270 #endif
271 watch_list = Qnil;
272 }
273
274 void
275 syms_of_gfilenotify (void)
276 {
277 defsubr (&Sgfile_add_watch);
278 defsubr (&Sgfile_rm_watch);
279 defsubr (&Sgfile_valid_p);
280
281 /* Filter objects. */
282 DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS */
283 DEFSYM (Qsend_moved, "send-moved"); /* G_FILE_MONITOR_SEND_MOVED */
284
285 /* Event types. */
286 DEFSYM (Qchanged, "changed"); /* G_FILE_MONITOR_EVENT_CHANGED */
287 DEFSYM (Qchanges_done_hint, "changes-done-hint");
288 /* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT */
289 DEFSYM (Qdeleted, "deleted"); /* G_FILE_MONITOR_EVENT_DELETED */
290 DEFSYM (Qcreated, "created"); /* G_FILE_MONITOR_EVENT_CREATED */
291 DEFSYM (Qattribute_changed, "attribute-changed");
292 /* G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED */
293 DEFSYM (Qpre_unmount, "pre-unmount"); /* G_FILE_MONITOR_EVENT_PRE_UNMOUNT */
294 DEFSYM (Qunmounted, "unmounted"); /* G_FILE_MONITOR_EVENT_UNMOUNTED */
295 DEFSYM (Qmoved, "moved"); /* G_FILE_MONITOR_EVENT_MOVED */
296
297 staticpro (&watch_list);
298
299 Fprovide (intern_c_string ("gfilenotify"), Qnil);
300
301 }
302
303 #endif /* HAVE_GFILENOTIFY */