about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--externaleditor.c372
-rw-r--r--freebsd/Makefile4
-rw-r--r--settings.c15
-rw-r--r--xxxterm.129
-rw-r--r--xxxterm.c15
-rw-r--r--xxxterm.conf9
-rw-r--r--xxxterm.h6
8 files changed, 447 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index f447ea2..5be51bc 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ PROG=xxxterm
 MAN=xxxterm.1
 
 SRCS= cookie.c inspector.c marco.c about.c whitelist.c settings.c inputfocus.c
-SRCS+= history.c completion.c xxxterm.c
+SRCS+= history.c completion.c externaleditor.c xxxterm.c
 CFLAGS+= -O2 -Wall -Wno-format-extra-args -Wunused
 CFLAGS+= -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare
 DEBUG= -ggdb3
diff --git a/externaleditor.c b/externaleditor.c
new file mode 100644
index 0000000..037f43b
--- /dev/null
+++ b/externaleditor.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2012 Elias Norberg <xyzzy@kudzu.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "xxxterm.h"
+
+#if WEBKIT_CHECK_VERSION(1, 5, 0)
+	/* we got the DOM API we need */
+
+struct edit_src_cb_args {
+		WebKitWebFrame		*frame;
+		WebKitWebDataSource	*data_src;
+};
+
+struct open_external_editor_cb_args {
+		int		child_pid;
+		char		*path;
+		time_t		mtime;
+		struct tab	*tab;
+		int		(*callback)(const char *,gpointer);
+		gpointer	cb_data;
+};
+
+int
+open_external_editor_cb (gpointer data)
+{
+	struct open_external_editor_cb_args	*args;
+	struct stat				st;
+	struct tab				*t;
+
+	int					fd;
+	int					rv, nb;
+	int					status;
+	int					found_tab = 0;
+	GString					*contents = NULL;
+
+	contents = NULL;
+
+	args = (struct open_external_editor_cb_args*)data;
+
+	/* Check if tab is still open */
+	TAILQ_FOREACH(t, &tabs, entry)
+		if (t == args->tab) {
+			found_tab = 1;
+			break;
+		}
+
+	/* Tab was deleted */
+	if (!found_tab){
+		g_free(args->path);
+		g_free(args->cb_data);
+		g_free(args);
+		return (0);
+	}
+
+	rv = stat(args->path, &st);
+	if (rv == -1 && errno != ENOENT) {
+		DPRINTF("open_external_editor_cb: stat error, file %s, error: %s\n", args->path, strerror(errno));
+		show_oops(args->tab, "stat error : %s", strerror(errno));
+
+		g_free(args->path);
+		g_free(args->cb_data);
+		g_free(args);
+		return (0);
+	}else if ( rv == 0 && st.st_mtime > args->mtime) {
+		DPRINTF("File %s has been modified\n", args->path);
+		args->mtime = st.st_mtime;
+
+		contents = g_string_sized_new(1024);
+		fd = open(args->path, O_RDONLY);
+		if (fd == -1) {
+			DPRINTF("open_external_editor_cb, open error, %s\n", strerror(errno));
+			g_free(args->path);
+			g_free(args->cb_data);
+			g_free(args);
+			return (0);
+		}
+
+		char buf[1024];
+		nb = 0;
+		for (;;) {
+			nb = read(fd, buf, 1024);
+			if (nb < 0) {
+				g_string_free(contents, TRUE);
+				show_oops(args->tab,strerror(errno));
+				g_free(args->path);
+				g_free(args->cb_data);
+				g_free(args);
+				return (0);
+			}
+
+			buf[nb] = '\0';
+			contents = g_string_append(contents, buf);
+
+			if (nb < 1024)
+				break;
+		}
+		close(fd);
+
+		DPRINTF("external_editor_cb: contents updated\n");
+		if (args->callback)
+			args->callback(contents->str, args->cb_data);
+
+		g_string_free(contents, TRUE);
+	}
+
+	/* Check if child has terminated */
+	rv = waitpid(args->child_pid, &status, WNOHANG);
+	if (rv == -1 || WIFEXITED(status)) {
+
+		/* Delete the file */
+		unlink(args->path);
+		g_free(args->path);
+		g_free(args->cb_data);
+		g_free(args);
+		return (0);
+	}
+
+	return (1);
+}
+
+int
+open_external_editor(struct tab *t, const char *contents, const char *suffix,
+    int (*callback)(const char *, gpointer), gpointer cb_data)
+{
+	struct open_external_editor_cb_args	*a;
+	char					command[PATH_MAX];
+	char					*filename;
+	char					*ptr;
+	int					fd;
+	int					nb, rv;
+	int					cnt;
+	struct stat				st;
+	pid_t					pid;
+
+	if (external_editor == NULL)
+		return (0);
+
+	if (suffix == NULL)
+		suffix = "";
+
+	filename = g_malloc(strlen(temp_dir) + strlen("/xxxtermXXXXXX") + strlen(suffix) + 1);
+	sprintf(filename, "%s/xxxtermXXXXXX%s", temp_dir, suffix);
+
+	/* Create a temporary file */
+	fd = mkstemps(filename,strlen(suffix));
+	if (fd == -1) {
+		show_oops(t, "Cannot create temporary file");
+		return (1);
+	}
+
+	nb = 0;
+	while (nb < strlen(contents)) {
+		if (strlen(contents) - nb > 1024)
+			cnt = 1024;
+		else
+			cnt = strlen(contents) - nb;
+
+		rv = write(fd, contents+nb, cnt);
+		if (rv < 0) {
+			g_free(filename);
+			close(fd);
+			show_oops(t,strerror(errno));
+			return (1);
+		}
+
+		nb += rv;
+	}
+
+	rv = fstat(fd, &st);
+	if (rv == -1){
+		g_free(filename);
+		close(fd);
+		show_oops(t,"Cannot stat file: %s\n", strerror(errno));
+		return (1);
+	}
+	close(fd);
+
+	DPRINTF("edit_src: external_editor: %s\n", external_editor);
+
+	nb = 0;
+	for (ptr = external_editor; nb < sizeof(command) - 1 && *ptr; ptr++) {
+
+		if (*ptr == '<') {
+			if (strncasecmp(ptr, "<file>", 6) == 0) {
+				strncpy(command+nb, filename, sizeof(command) - nb - 1);
+				ptr += 5;
+				nb += strlen(filename);
+			}
+		}else
+			command[nb++] = *ptr;
+	}
+	command[nb] = '\0';
+
+	DPRINTF("edit_src: Launching program %s\n", command);
+
+	/* Launch editor */
+	pid = fork();
+	switch (pid) {
+	case -1:
+		show_oops(t, "can't fork to execute editor");
+		return (1);
+	case 0:
+		break;
+	default:
+
+		a = g_malloc(sizeof(struct open_external_editor_cb_args));
+		a->child_pid = pid;
+		a->path = filename;
+		a->tab = t;
+		a->mtime = st.st_mtime;
+		a->callback = callback;
+		a->cb_data = cb_data;
+
+		/* Check every 100 ms if file has changed */
+		g_timeout_add(100, (GSourceFunc)open_external_editor_cb, (gpointer)a);
+		return (0);
+	}
+
+	/* child */
+	rv = system(command);
+
+	_exit(0);
+	/* NOTREACHED */
+	return (0);
+}
+
+int
+edit_src_cb(const char *contents, gpointer data)
+{
+	struct edit_src_cb_args *args;
+
+	args = (struct edit_src_cb_args *)data;
+
+	webkit_web_frame_load_string(args->frame, contents, NULL,
+	    webkit_web_data_source_get_encoding(args->data_src),
+	    webkit_web_frame_get_uri(args->frame));
+	return (0);
+}
+
+int
+edit_src(struct tab *t, struct karg *args)
+{
+	WebKitWebFrame		*frame;
+	WebKitWebDataSource	*ds;
+	GString			*contents;
+	struct edit_src_cb_args	*ext_args;
+
+	if (external_editor == NULL){
+		show_oops(t,"Setting external_editor not set");
+		return (1);
+	}
+
+	frame = webkit_web_view_get_focused_frame(t->wv);
+	ds = webkit_web_frame_get_data_source(frame);
+	if (webkit_web_data_source_is_loading(ds)) {
+		show_oops(t,"Webpage is still loading.");
+		return (1);
+	}
+
+	contents = webkit_web_data_source_get_data(ds);
+	if (!contents)
+		show_oops(t,"No contents - opening empty file");
+
+	ext_args = g_malloc(sizeof(struct edit_src_cb_args));
+	ext_args->frame = frame;
+	ext_args->data_src = ds;
+
+	/* Check every 100 ms if file has changed */
+	open_external_editor(t, contents->str, ".html", &edit_src_cb, ext_args);
+	return (0);
+}
+
+struct edit_element_cb_args {
+	WebKitDOMElement	*active;
+	struct tab		*tab;
+};
+
+int
+edit_element_cb(const char *contents, gpointer data)
+{
+	struct				edit_element_cb_args *args;
+	WebKitDOMHTMLTextAreaElement	*ta;
+	WebKitDOMHTMLInputElement	*el;
+
+	args = (struct edit_element_cb_args*)data;
+
+	if (!args || !args->active)
+		return (0);
+
+	el = (WebKitDOMHTMLInputElement*)args->active;
+	ta = (WebKitDOMHTMLTextAreaElement*)args->active;
+
+	if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el))
+		webkit_dom_html_input_element_set_value(el, contents);
+	else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta))
+		webkit_dom_html_text_area_element_set_value(ta, contents);
+
+	return (0);
+}
+
+int
+edit_element(struct tab *t, struct karg *a)
+{
+	WebKitDOMHTMLDocument		*doc;
+	WebKitDOMElement		*active_element;
+	WebKitDOMHTMLTextAreaElement	*ta;
+	WebKitDOMHTMLInputElement	*el;
+	char				*contents;
+	struct edit_element_cb_args	*args;
+
+	if (external_editor == NULL){
+		show_oops(t,"Setting external_editor not set");
+		return (0);
+	}
+
+	doc = (WebKitDOMHTMLDocument*)webkit_web_view_get_dom_document(t->wv);
+	active_element = webkit_dom_html_document_get_active_element(doc);
+	el = (WebKitDOMHTMLInputElement*)active_element;
+	ta = (WebKitDOMHTMLTextAreaElement*)active_element;
+
+	if (doc == NULL || active_element == NULL ||
+	    (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el) == 0 &&
+	    WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta) == 0)) {
+		show_oops(t, "No active text element!");
+		return (1);
+	}
+
+	contents = "";
+	if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el))
+		contents = webkit_dom_html_input_element_get_value(el);
+	else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta))
+		contents = webkit_dom_html_text_area_element_get_value(ta);
+
+	if ((args = g_malloc(sizeof(struct edit_element_cb_args))) == NULL)
+		return (1);
+
+	args->tab = t;
+	args->active = active_element;
+
+	open_external_editor(t, contents, NULL, &edit_element_cb,  args);
+
+	return (0);
+}
+
+#else /* Just to make things compile. */
+
+int
+edit_element(struct tab *t, struct karg *a)
+{
+	return (1);
+}
+
+int
+edit_src(struct tab *t, struct karg *args)
+{
+	return (1);
+}
+
+#endif
diff --git a/freebsd/Makefile b/freebsd/Makefile
index 21d9bb1..ebb51e6 100644
--- a/freebsd/Makefile
+++ b/freebsd/Makefile
@@ -39,10 +39,12 @@ history.o: ../history.o
 
 completion.o: ../completion.o
 
+externaleditor.o: ../externaleditor.o
+
 ../xxxterm.o: ../javascript.h
 
 xxxterm: xxxterm.o freebsd.o marco.o about.o inspector.o whitelist.o settings.o \
-	cookie.o history.o completion.o inputfocus.o
+	cookie.o history.o completion.o inputfocus.o externaleditor.o
 	$(CC) $(LDFLAGS) -o $@ *.o $+ $(LDADD)
 
 install: all
diff --git a/settings.c b/settings.c
index fd70b80..b566bb8 100644
--- a/settings.c
+++ b/settings.c
@@ -95,6 +95,7 @@ int		auto_load_images = 1;
 int		enable_autoscroll = 0;
 int		enable_favicon_entry = 1;
 int		enable_favicon_tabs = 0;
+char		*external_editor = NULL;
 
 char		*cmd_font_name = NULL;
 char		*oops_font_name = NULL;
@@ -126,6 +127,7 @@ int		set_auto_load_images(char *value);
 int		set_enable_autoscroll(char *value);
 int		set_enable_favicon_entry(char *value);
 int		set_enable_favicon_tabs(char *value);
+int		set_external_editor(char *);
 
 void		walk_mime_type(struct settings *, void (*)(struct settings *,
 		    char *, void *), void *);
@@ -288,6 +290,7 @@ struct settings		rs[] = {
 	{ "enable_socket",		XT_S_INT, XT_SF_RESTART,&enable_socket, NULL, NULL },
 	{ "enable_spell_checking",	XT_S_INT, 0,		&enable_spell_checking, NULL, NULL },
 	{ "encoding",			XT_S_STR, 0, NULL,	&encoding, NULL },
+	{ "external_editor",		XT_S_STR,0, NULL,	&external_editor, NULL, NULL, set_external_editor },
 	{ "fancy_bar",			XT_S_INT, XT_SF_RESTART,&fancy_bar, NULL, NULL },
 	{ "guess_search",		XT_S_INT, 0,		&guess_search, NULL, NULL },
 	{ "history_autosave",		XT_S_INT, 0,		&history_autosave, NULL, NULL },
@@ -732,6 +735,7 @@ struct key_binding	keys[] = {
 	{ "help",		0,	1,	GDK_F1		},
 	{ "run_script",		MOD1,	1,	GDK_r		},
 	{ "proxy toggle",	0,	1,	GDK_F2		},
+	{ "editelement",	CTRL,	1,	GDK_i		},
 
 	/* search */
 	{ "searchnext",		0,	0,	GDK_n		},
@@ -1060,6 +1064,17 @@ set_enable_favicon_tabs(char *value)
 	return (0);
 }
 
+int
+set_external_editor(char *editor)
+{
+	if (external_editor)
+		g_free(external_editor);
+
+	external_editor = g_strdup(editor);
+
+	return (0);
+}
+
 void
 setup_proxy(char *uri)
 {
diff --git a/xxxterm.1 b/xxxterm.1
index c44d17b..6a67939 100644
--- a/xxxterm.1
+++ b/xxxterm.1
@@ -404,6 +404,14 @@ Toggle the global page style mode.
 Will also affect new tabs.
 .Pq Cm userstyle_global
 .El
+.Ss Insert-mode commands
+The following commands are only available when editing an input-field
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It Cm C-i
+Edit the contents of the currently active input-element in an external editor.
+.Pq Cm editelement
+.El
 .Sh COMMAND MODE
 Command mode works in a similar fashion to the
 .Xr vi 1
@@ -470,6 +478,17 @@ If
 is not set
 .Nm
 will display the current tab encoding.
+.It Cm editsrc
+Opens the source for the current tab in the editor specified by the
+setting
+.Cm external_editor
+and then checks for changes to the file opened. If it is changed, the
+page will be updated.
+.It Cm editelement
+If a text-element is currently active (<input> or <textarea>), it's
+contents will be opened in the same fashion as for the command
+.Cm editsrc
+above
 .It Cm fav
 Show favorites.
 .It Cm favadd
@@ -1053,6 +1072,16 @@ option.
 Set the default encoding.
 E.g.
 .Pa encoding = ISO-8859-1 .
+.It Cm external_editor
+Set which editor to use for external editing.
+the string <file> will be replaced by the current filename.
+E.g.
+.Pa external_editor = gvim -f <file>
+Note!
+.Cm xxxterm
+relies on the editor
+.Pa not forking
+into the background.
 .It Cm fancy_bar
 Enables a backward, forward, and stop button to the toolbar.
 Additionally if
diff --git a/xxxterm.c b/xxxterm.c
index b5098b4..a6ac17f 100644
--- a/xxxterm.c
+++ b/xxxterm.c
@@ -85,6 +85,7 @@ TAILQ_HEAD(command_list, command_entry);
 #define XT_CERT_DIR		("certs/")
 #define XT_JS_DIR		("js/")
 #define XT_SESSIONS_DIR		("sessions/")
+#define XT_TEMP_DIR		("tmp")
 #define XT_CONF_FILE		("xxxterm.conf")
 #define XT_QMARKS_FILE		("quickmarks")
 #define XT_SAVED_TABS_FILE	("main_session")
@@ -649,6 +650,7 @@ char			certs_dir[PATH_MAX];
 char			js_dir[PATH_MAX];
 char			cache_dir[PATH_MAX];
 char			sessions_dir[PATH_MAX];
+char			temp_dir[PATH_MAX];
 char			cookie_file[PATH_MAX];
 SoupSession		*session;
 SoupCookieJar		*s_cookiejar;
@@ -788,7 +790,7 @@ guess_url_type(char *url_in)
 			}
 			free(cwd);
 		}
-	}else
+	} else
 		url_out = g_strdup_printf("http://%s", url_in); /* guess http */
 done:
 	DNPRINTF(XT_D_URL, "guess_url_type: guessed %s\n", url_out);
@@ -2927,6 +2929,8 @@ struct cmd {
 	{ "hinting",		0,	command,		'.',			0 },
 	{ "hinting_newtab",	0,	command,		',',			0 },
 	{ "togglesrc",		0,	toggle_src,		0,			0 },
+	{ "editsrc",		0,	edit_src,		0,			0 },
+	{ "editelement",	0,	edit_element,		0,			0 },
 
 	/* yanking and pasting */
 	{ "yankuri",		0,	yank_uri,		0,			0 },
@@ -5173,9 +5177,10 @@ cmd_getlist(int id, char *key)
 			return;
 		} else if (cmds[id].type & XT_SETARG) {
 			for (i = 0; i < get_settings_size(); i++)
-				if(!strncmp(key, get_setting_name(i),
+				if (!strncmp(key, get_setting_name(i),
 				    strlen(key)))
-					cmd_status.list[c++] = get_setting_name(i);
+					cmd_status.list[c++] =
+					    get_setting_name(i);
 			cmd_status.len = c;
 			return;
 		}
@@ -7295,6 +7300,10 @@ main(int argc, char *argv[])
 	snprintf(js_dir, sizeof js_dir, "%s/%s", work_dir, XT_JS_DIR);
 	xxx_dir(js_dir);
 
+	/* temp dir */
+	snprintf(temp_dir, sizeof temp_dir, "%s/%s", work_dir, XT_TEMP_DIR);
+	xxx_dir(temp_dir);
+
 	/* runtime settings that can override config file */
 	if (runtime_settings[0] != '\0')
 		config_parse(runtime_settings, 1);
diff --git a/xxxterm.conf b/xxxterm.conf
index 9d566b5..b90c514 100644
--- a/xxxterm.conf
+++ b/xxxterm.conf
@@ -67,6 +67,14 @@
 # Yahoo
 # search_string		= http://search.yahoo.com/search?p=%s
 
+# External editor.
+# NOTE: The editor specified here must not fork into the background.
+#
+# Examples of editors:
+# external_editor	= gvim -f <file>
+# external_editor	= xterm -geometry 177x58+0+0 -font -*-fixed-medium-r-*-*-15-*-*-*-*-*-iso8859-* -e vim -f <file>
+# external_editor	= emacsclient -c <file>
+
 # "default_script" points to a script executed by the run_script
 # command. The only argument passed to this script is the current URI.
 #
@@ -334,3 +342,4 @@ user_agent		= Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1 like Mac OS X; en-us) Ap
 # keybinding	= focusin,C-equal
 # keybinding	= focusin,C-plus
 # keybinding	= focusreset,C-0
+# keybinding	= editelement,!C-i
diff --git a/xxxterm.h b/xxxterm.h
index d048cfd..618ccc4 100644
--- a/xxxterm.h
+++ b/xxxterm.h
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <dirent.h>
+#include <fcntl.h>
 
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -299,6 +300,10 @@ int			color_visited(struct tab *t, char *visited);
 void			completion_add(struct tab *);
 void			completion_add_uri(const gchar *uri);
 
+/* external editor */
+int			edit_src(struct tab *t, struct karg *args);
+int			edit_element(struct tab *t, struct karg *a);
+
 /* proxy */
 #define XT_PRXY_SHOW		(1<<0)
 #define XT_PRXY_TOGGLE		(1<<1)
@@ -539,6 +544,7 @@ extern int	append_next;
 extern char	*home;
 extern char	*search_string;
 extern char	*http_proxy;
+extern char	*external_editor;
 extern char	download_dir[PATH_MAX];
 extern char	runtime_settings[PATH_MAX];
 extern int	allow_volatile_cookies;