diff options
author | Stevan Andjelkovic <stevan.andjelkovic@strath.ac.uk> | 2012-01-03 23:27:24 +0100 |
---|---|---|
committer | Stevan Andjelkovic <stevan.andjelkovic@strath.ac.uk> | 2012-01-03 23:27:24 +0100 |
commit | 02e49e429bf3990f81373b61dd3044acc20cd393 (patch) | |
tree | ca543622585f684a836875137d17bf32c6517adc | |
parent | cfd919ea2eb2fcfca2f2759cde43b1aeec539d84 (diff) | |
download | xombrero-02e49e429bf3990f81373b61dd3044acc20cd393.tar.gz |
Merge external editor stuff.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | externaleditor.c | 372 | ||||
-rw-r--r-- | freebsd/Makefile | 4 | ||||
-rw-r--r-- | settings.c | 15 | ||||
-rw-r--r-- | xxxterm.1 | 29 | ||||
-rw-r--r-- | xxxterm.c | 15 | ||||
-rw-r--r-- | xxxterm.conf | 9 | ||||
-rw-r--r-- | xxxterm.h | 6 |
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; |