Prepare for release 1.6.1.
[xombrero.git] / externaleditor.c
blob776f8fd25aab120b692fda592a1fbb89da72c9fa
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);
77 DPRINTF("external_editor_cb: contents updated\n");
78 if (args->callback)
79 args->callback(contents->str, args->cb_data);
81 g_string_free(contents, TRUE);
83 return (0);
86 done:
87 if (fd != -1)
88 close(fd);
89 return (0);
92 void
93 external_editor_closed(GPid pid, gint status, gpointer data)
95 struct external_editor_args *args;
96 struct tab *t;
97 int found_tab = 0;
99 args = (struct external_editor_args *)data;
101 TAILQ_FOREACH(t, &tabs, entry)
102 if (t == args->tab) {
103 found_tab = 1;
104 break;
107 /* Tab was deleted */
108 if (!found_tab)
109 goto done;
112 * unfortunately we can't check the exit status in glib < 2.34,
113 * otherwise a check and warning would be nice here
115 update_contents(args);
117 done:
118 unlink(args->path);
119 g_spawn_close_pid(pid);
123 open_external_editor_cb(gpointer data)
125 struct external_editor_args *args;
126 struct tab *t;
127 int found_tab = 0;
129 args = (struct external_editor_args*)data;
131 /* Check if tab is still open */
132 TAILQ_FOREACH(t, &tabs, entry)
133 if (t == args->tab) {
134 found_tab = 1;
135 break;
138 /* Tab was deleted */
139 if (!found_tab)
140 goto done;
142 if (update_contents(args))
143 goto done;
145 return (1);
146 done:
147 /* cleanup and remove from event loop */
148 g_free(args->path);
149 g_free(args->cb_data);
150 g_free(args);
152 return (0);
156 open_external_editor(struct tab *t, const char *contents,
157 int (*callback)(const char *, gpointer), gpointer cb_data)
159 struct stat st;
160 struct external_editor_args *a;
161 GPid pid;
162 char *cmdstr;
163 char filename[PATH_MAX];
164 char **sv;
165 int fd;
166 int nb, rv;
167 int cnt;
169 if (external_editor == NULL)
170 return (0);
172 snprintf(filename, sizeof filename, "%s" PS "xombreroXXXXXX", temp_dir);
174 /* Create a temporary file */
175 fd = g_mkstemp(filename);
176 if (fd == -1) {
177 show_oops(t, "Cannot create temporary file");
178 return (1);
181 nb = 0;
182 while (contents && nb < strlen(contents)) {
183 if (strlen(contents) - nb > XT_EE_BUFSZ)
184 cnt = XT_EE_BUFSZ;
185 else
186 cnt = strlen(contents) - nb;
188 rv = write(fd, contents+nb, cnt);
189 if (rv < 0) {
190 close(fd);
191 show_oops(t,strerror(errno));
192 return (1);
195 nb += rv;
198 rv = fstat(fd, &st);
199 if (rv == -1) {
200 close(fd);
201 show_oops(t,"Cannot stat file: %s\n", strerror(errno));
202 return (1);
204 close(fd);
206 DPRINTF("edit_src: external_editor: %s\n", external_editor);
208 sv = g_strsplit(external_editor, "<file>", -1);
209 cmdstr = g_strjoinv(filename, sv);
210 g_strfreev(sv);
211 sv = g_strsplit_set(cmdstr, " \t", -1);
212 if (!g_spawn_async(NULL, sv, NULL,
213 (G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD), NULL, NULL, &pid,
214 NULL)) {
215 show_oops(t, "%s: could not spawn process");
216 g_strfreev(sv);
217 g_free(cmdstr);
218 return (1);
221 g_strfreev(sv);
222 g_free(cmdstr);
224 a = g_malloc(sizeof(struct external_editor_args));
225 a->child_pid = pid;
226 a->path = g_strdup(filename);
227 a->tab = t;
228 a->mtime = st.st_mtime;
229 a->callback = callback;
230 a->cb_data = cb_data;
232 /* Check every 100 ms if file has changed */
233 g_timeout_add(100, (GSourceFunc)open_external_editor_cb,
234 (gpointer)a);
236 /* Stop loop child has terminated */
237 g_child_watch_add(pid, external_editor_closed, (gpointer)a);
239 return (0);
243 edit_src_cb(const char *contents, gpointer data)
245 struct edit_src_cb_args *args;
247 args = (struct edit_src_cb_args *)data;
249 webkit_web_frame_load_string(args->frame, contents, NULL,
250 webkit_web_data_source_get_encoding(args->data_src),
251 webkit_web_frame_get_uri(args->frame));
252 return (0);
256 edit_src(struct tab *t, struct karg *args)
258 WebKitWebFrame *frame;
259 WebKitWebDataSource *ds;
260 GString *contents;
261 struct edit_src_cb_args *ext_args;
263 if (external_editor == NULL){
264 show_oops(t,"Setting external_editor not set");
265 return (1);
268 frame = webkit_web_view_get_focused_frame(t->wv);
269 ds = webkit_web_frame_get_data_source(frame);
270 if (webkit_web_data_source_is_loading(ds)) {
271 show_oops(t,"Webpage is still loading.");
272 return (1);
275 contents = webkit_web_data_source_get_data(ds);
276 if (!contents)
277 show_oops(t,"No contents - opening empty file");
279 ext_args = g_malloc(sizeof(struct edit_src_cb_args));
280 ext_args->frame = frame;
281 ext_args->data_src = ds;
283 /* Check every 100 ms if file has changed */
284 open_external_editor(t, contents ? contents->str : "", &edit_src_cb,
285 ext_args);
286 return (0);
289 struct edit_element_cb_args {
290 WebKitDOMElement *active;
291 struct tab *tab;
295 edit_element_cb(const char *contents, gpointer data)
297 struct edit_element_cb_args *args;
298 WebKitDOMHTMLTextAreaElement *ta;
299 WebKitDOMHTMLInputElement *el;
301 args = (struct edit_element_cb_args*)data;
303 if (!args || !args->active)
304 return (0);
306 el = (WebKitDOMHTMLInputElement*)args->active;
307 ta = (WebKitDOMHTMLTextAreaElement*)args->active;
309 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el))
310 webkit_dom_html_input_element_set_value(el, contents);
311 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta))
312 webkit_dom_html_text_area_element_set_value(ta, contents);
314 return (0);
318 edit_element(struct tab *t, struct karg *a)
320 WebKitDOMHTMLDocument *doc;
321 WebKitDOMElement *active_element;
322 WebKitDOMHTMLTextAreaElement *ta;
323 WebKitDOMHTMLInputElement *el;
324 char *contents;
325 struct edit_element_cb_args *args;
327 if (external_editor == NULL){
328 show_oops(t,"Setting external_editor not set");
329 return (0);
332 doc = (WebKitDOMHTMLDocument*)webkit_web_view_get_dom_document(t->wv);
333 active_element = webkit_dom_html_document_get_active_element(doc);
334 el = (WebKitDOMHTMLInputElement*)active_element;
335 ta = (WebKitDOMHTMLTextAreaElement*)active_element;
337 if (doc == NULL || active_element == NULL ||
338 (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el) == 0 &&
339 WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta) == 0)) {
340 show_oops(t, "No active text element!");
341 return (1);
344 contents = "";
345 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el))
346 contents = webkit_dom_html_input_element_get_value(el);
347 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta))
348 contents = webkit_dom_html_text_area_element_get_value(ta);
350 if ((args = g_malloc(sizeof(struct edit_element_cb_args))) == NULL)
351 return (1);
353 args->tab = t;
354 args->active = active_element;
356 open_external_editor(t, contents, &edit_element_cb, args);
358 return (0);
361 #else /* Just to make things compile. */
364 edit_element(struct tab *t, struct karg *a)
366 show_oops(t, "external editor feature requires webkit >= 1.5.0");
367 return (1);
371 edit_src(struct tab *t, struct karg *args)
373 show_oops(t, "external editor feature requires webkit >= 1.5.0");
374 return (1);
377 #endif