Prepare for release 1.6.0.
[xombrero.git] / externaleditor.c
blobec56b7e5ff64a867e2654148cca006787cce4d27
1 /*
2 * Copyright (c) 2012 Elias Norberg <xyzzy@kudzu.se>
3 * Copyright (c) 2012 Josh Rickmar <jrick@devio.us>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <xombrero.h>
20 #if WEBKIT_CHECK_VERSION(1, 5, 0)
21 /* we got the DOM API we need */
23 struct edit_src_cb_args {
24 WebKitWebFrame *frame;
25 WebKitWebDataSource *data_src;
28 struct external_editor_args {
29 GPid child_pid;
30 char *path;
31 time_t mtime;
32 struct tab *tab;
33 int (*callback)(const char *,gpointer);
34 gpointer cb_data;
37 int
38 update_contents(struct external_editor_args *args)
40 struct stat st;
41 int fd = -1;
42 int rv, nb;
43 GString *contents = NULL;
44 char buf[XT_EE_BUFSZ];
46 rv = stat(args->path, &st);
47 if (rv == -1 && errno == ENOENT)
48 return (1);
49 else if (rv == 0 && st.st_mtime > args->mtime) {
50 DPRINTF("File %s has been modified\n", args->path);
51 args->mtime = st.st_mtime;
53 contents = g_string_sized_new(XT_EE_BUFSZ);
54 fd = open(args->path, O_RDONLY);
55 if (fd == -1) {
56 DPRINTF("open_external_editor_cb, open error, %s\n",
57 strerror(errno));
58 goto done;
61 for (;;) {
62 nb = read(fd, buf, XT_EE_BUFSZ);
63 if (nb < 0) {
64 g_string_free(contents, TRUE);
65 show_oops(args->tab, strerror(errno));
66 goto done;
69 buf[nb] = '\0';
70 contents = g_string_append(contents, buf);
72 if (nb < XT_EE_BUFSZ)
73 break;
75 close(fd);
76 fd = -1;
78 DPRINTF("external_editor_cb: contents updated\n");
79 if (args->callback)
80 args->callback(contents->str, args->cb_data);
82 g_string_free(contents, TRUE);
84 return (0);
87 done:
88 if (fd != -1)
89 close(fd);
90 return (0);
93 void
94 external_editor_closed(GPid pid, gint status, gpointer data)
96 struct external_editor_args *args;
97 struct tab *t;
98 int found_tab = 0;
100 args = (struct external_editor_args *)data;
102 TAILQ_FOREACH(t, &tabs, entry)
103 if (t == args->tab) {
104 found_tab = 1;
105 break;
108 /* Tab was deleted */
109 if (!found_tab)
110 goto done;
113 * unfortunately we can't check the exit status in glib < 2.34,
114 * otherwise a check and warning would be nice here
116 update_contents(args);
118 done:
119 unlink(args->path);
120 g_spawn_close_pid(pid);
124 open_external_editor_cb(gpointer data)
126 struct external_editor_args *args;
127 struct tab *t;
128 int found_tab = 0;
130 args = (struct external_editor_args*)data;
132 /* Check if tab is still open */
133 TAILQ_FOREACH(t, &tabs, entry)
134 if (t == args->tab) {
135 found_tab = 1;
136 break;
139 /* Tab was deleted */
140 if (!found_tab)
141 goto done;
143 if (update_contents(args))
144 goto done;
146 return (1);
147 done:
148 /* cleanup and remove from event loop */
149 g_free(args->path);
150 g_free(args->cb_data);
151 g_free(args);
153 return (0);
157 open_external_editor(struct tab *t, const char *contents,
158 int (*callback)(const char *, gpointer), gpointer cb_data)
160 struct stat st;
161 struct external_editor_args *a;
162 GPid pid;
163 char *cmdstr;
164 char filename[PATH_MAX];
165 char **sv;
166 int fd;
167 int nb, rv;
168 int cnt;
170 if (external_editor == NULL)
171 return (0);
173 snprintf(filename, sizeof filename, "%s" PS "xombreroXXXXXX", temp_dir);
175 /* Create a temporary file */
176 fd = g_mkstemp(filename);
177 if (fd == -1) {
178 show_oops(t, "Cannot create temporary file");
179 return (1);
182 nb = 0;
183 while (contents && nb < strlen(contents)) {
184 if (strlen(contents) - nb > XT_EE_BUFSZ)
185 cnt = XT_EE_BUFSZ;
186 else
187 cnt = strlen(contents) - nb;
189 rv = write(fd, contents+nb, cnt);
190 if (rv < 0) {
191 close(fd);
192 show_oops(t,strerror(errno));
193 return (1);
196 nb += rv;
199 rv = fstat(fd, &st);
200 if (rv == -1) {
201 close(fd);
202 show_oops(t,"Cannot stat file: %s\n", strerror(errno));
203 return (1);
205 close(fd);
207 DPRINTF("edit_src: external_editor: %s\n", external_editor);
209 sv = g_strsplit(external_editor, "<file>", -1);
210 cmdstr = g_strjoinv(filename, sv);
211 g_strfreev(sv);
212 sv = g_strsplit_set(cmdstr, " \t", -1);
213 if (!g_spawn_async(NULL, sv, NULL,
214 (G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD), NULL, NULL, &pid,
215 NULL)) {
216 show_oops(t, "%s: could not spawn process");
217 g_strfreev(sv);
218 g_free(cmdstr);
219 return (1);
222 g_strfreev(sv);
223 g_free(cmdstr);
225 a = g_malloc(sizeof(struct external_editor_args));
226 a->child_pid = pid;
227 a->path = g_strdup(filename);
228 a->tab = t;
229 a->mtime = st.st_mtime;
230 a->callback = callback;
231 a->cb_data = cb_data;
233 /* Check every 100 ms if file has changed */
234 g_timeout_add(100, (GSourceFunc)open_external_editor_cb,
235 (gpointer)a);
237 /* Stop loop child has terminated */
238 g_child_watch_add(pid, external_editor_closed, (gpointer)a);
240 return (0);
244 edit_src_cb(const char *contents, gpointer data)
246 struct edit_src_cb_args *args;
248 args = (struct edit_src_cb_args *)data;
250 webkit_web_frame_load_string(args->frame, contents, NULL,
251 webkit_web_data_source_get_encoding(args->data_src),
252 webkit_web_frame_get_uri(args->frame));
253 return (0);
257 edit_src(struct tab *t, struct karg *args)
259 WebKitWebFrame *frame;
260 WebKitWebDataSource *ds;
261 GString *contents;
262 struct edit_src_cb_args *ext_args;
264 if (external_editor == NULL){
265 show_oops(t,"Setting external_editor not set");
266 return (1);
269 frame = webkit_web_view_get_focused_frame(t->wv);
270 ds = webkit_web_frame_get_data_source(frame);
271 if (webkit_web_data_source_is_loading(ds)) {
272 show_oops(t,"Webpage is still loading.");
273 return (1);
276 contents = webkit_web_data_source_get_data(ds);
277 if (!contents)
278 show_oops(t,"No contents - opening empty file");
280 ext_args = g_malloc(sizeof(struct edit_src_cb_args));
281 ext_args->frame = frame;
282 ext_args->data_src = ds;
284 /* Check every 100 ms if file has changed */
285 open_external_editor(t, contents ? contents->str : "", &edit_src_cb,
286 ext_args);
287 return (0);
290 struct edit_element_cb_args {
291 WebKitDOMElement *active;
292 struct tab *tab;
296 edit_element_cb(const char *contents, gpointer data)
298 struct edit_element_cb_args *args;
299 WebKitDOMHTMLTextAreaElement *ta;
300 WebKitDOMHTMLInputElement *el;
302 args = (struct edit_element_cb_args*)data;
304 if (!args || !args->active)
305 return (0);
307 el = (WebKitDOMHTMLInputElement*)args->active;
308 ta = (WebKitDOMHTMLTextAreaElement*)args->active;
310 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el))
311 webkit_dom_html_input_element_set_value(el, contents);
312 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta))
313 webkit_dom_html_text_area_element_set_value(ta, contents);
315 return (0);
319 edit_element(struct tab *t, struct karg *a)
321 WebKitDOMHTMLDocument *doc;
322 WebKitDOMElement *active_element;
323 WebKitDOMHTMLTextAreaElement *ta;
324 WebKitDOMHTMLInputElement *el;
325 char *contents;
326 struct edit_element_cb_args *args;
328 if (external_editor == NULL){
329 show_oops(t,"Setting external_editor not set");
330 return (0);
333 doc = (WebKitDOMHTMLDocument*)webkit_web_view_get_dom_document(t->wv);
334 active_element = webkit_dom_html_document_get_active_element(doc);
335 el = (WebKitDOMHTMLInputElement*)active_element;
336 ta = (WebKitDOMHTMLTextAreaElement*)active_element;
338 if (doc == NULL || active_element == NULL ||
339 (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el) == 0 &&
340 WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta) == 0)) {
341 show_oops(t, "No active text element!");
342 return (1);
345 contents = "";
346 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el))
347 contents = webkit_dom_html_input_element_get_value(el);
348 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta))
349 contents = webkit_dom_html_text_area_element_get_value(ta);
351 if ((args = g_malloc(sizeof(struct edit_element_cb_args))) == NULL)
352 return (1);
354 args->tab = t;
355 args->active = active_element;
357 open_external_editor(t, contents, &edit_element_cb, args);
359 return (0);
362 #else /* Just to make things compile. */
365 edit_element(struct tab *t, struct karg *a)
367 show_oops(t, "external editor feature requires webkit >= 1.5.0");
368 return (1);
372 edit_src(struct tab *t, struct karg *args)
374 show_oops(t, "external editor feature requires webkit >= 1.5.0");
375 return (1);
378 #endif