From: Robert Timm Date: Thu, 11 May 2017 21:15:45 +0000 (+0200) Subject: implements external editor (closes #347) X-Git-Url: https://git.owens.tech///git?a=commitdiff_plain;h=c5412c9756d7ff09e0da728e2794cd0cc412383e;p=vimb.git implements external editor (closes #347) --- diff --git a/src/input.c b/src/input.c index 3e7943c..f0ece14 100644 --- a/src/input.c +++ b/src/input.c @@ -28,6 +28,12 @@ #include "util.h" #include "ext-proxy.h" +typedef struct { + Client *c; + char *file; +} EditorData; + +static void resume_editor(GPid pid, int status, EditorData *data); /** * Function called when vimb enters the input mode. @@ -100,7 +106,147 @@ VbResult input_keypress(Client *c, int key) VbResult input_open_editor(Client *c) { - /* TODO should the editor be opened by the webextension or by the - * application? */ + char **argv, *file_path = NULL; + const char *text = NULL, *editor_command; + int argc; + GPid pid; + gboolean success; + GVariant *jsreturn; + + g_assert(c); + + /* get the editor command */ + editor_command = GET_CHAR(c, "editor-command"); + if (!editor_command || !*editor_command) { + vb_echo(c, MSG_ERROR, true, "No editor-command configured"); + return RESULT_ERROR; + } + + /* get the selected input element */ + jsreturn = ext_proxy_eval_script_sync(c, "vimb_input_mode_element.value"); + g_variant_get(jsreturn, "(bs)", &success, &text); + + if (!success || !text) { + return RESULT_ERROR; + } + + /* create a temp file to pass text to and from editor */ + if (!util_create_tmp_file(text, &file_path)) { + return RESULT_ERROR; + } + + /* spawn editor */ + char* command = g_strdup_printf(editor_command, file_path); + if (!g_shell_parse_argv(command, &argc, &argv, NULL)) { + g_critical("Could not parse editor-command '%s'", command); + g_free(command); + return RESULT_ERROR; + } + g_free(command); + + success = g_spawn_async( + NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &pid, NULL + ); + g_strfreev(argv); + + if (!success) { + unlink(file_path); + g_free(file_path); + g_warning("Could not spawn editor-command"); + return RESULT_ERROR; + } + + /* disable the active element */ + ext_proxy_eval_script(c, "vimb_input_mode_element.disabled=true", NULL); + + /* watch the editor process */ + EditorData *data = g_slice_new0(EditorData); + data->file = file_path; + data->c = c; + g_child_watch_add(pid, (GChildWatchFunc)resume_editor, data); + return RESULT_COMPLETE; } + +static void resume_editor(GPid pid, int status, EditorData *data) +{ + char *text, *tmp; + char *jscode; + + g_assert(pid); + g_assert(data); + g_assert(data->c); + g_assert(data->file); + + if (status == 0) { + /* get the text the editor stored */ + text = util_get_file_contents(data->file, NULL); + + if (text) { + /* escape the text to form a valid JS string */ + /* TODO: could go into util.c maybe */ + struct search_replace { + const char* search; + const char* replace; + } escapes[] = { + {"\x01", ""}, + {"\x02", ""}, + {"\x03", ""}, + {"\x04", ""}, + {"\x05", ""}, + {"\x06", ""}, + {"\a", ""}, + {"\b", ""}, + {"\t", "\\t"}, + {"\n", "\\n"}, + {"\v", ""}, + {"\f", ""}, + {"\r", ""}, + {"\x0E", ""}, + {"\x0F", ""}, + {"\x10", ""}, + {"\x11", ""}, + {"\x12", ""}, + {"\x13", ""}, + {"\x14", ""}, + {"\x15", ""}, + {"\x16", ""}, + {"\x17", ""}, + {"\x18", ""}, + {"\x19", ""}, + {"\x1A", ""}, + {"\x1B", ""}, + {"\x1C", ""}, + {"\x1D", ""}, + {"\x1E", ""}, + {"\x1F", ""}, + {"\"", "\\\""}, + {"\x7F", ""}, + {NULL, NULL}, + }; + + for(struct search_replace *i = escapes; i->search; i++) { + tmp = util_str_replace(i->search, i->replace, text); + g_free(text); + text = tmp; + } + + /* put the text back into the element */ + jscode = g_strdup_printf("vimb_input_mode_element.value=\"%s\"", text); + ext_proxy_eval_script(data->c, jscode, NULL); + + g_free(jscode); + g_free(text); + } + } + + ext_proxy_eval_script(data->c, + "vimb_input_mode_element.disabled=false;" + "vimb_input_mode_element.focus()", NULL); + + g_unlink(data->file); + g_free(data->file); + g_slice_free(EditorData, data); + g_spawn_close_pid(pid); +} diff --git a/src/util.c b/src/util.c index f91dd89..54bc416 100644 --- a/src/util.c +++ b/src/util.c @@ -110,6 +110,42 @@ gboolean util_create_dir_if_not_exists(const char *dirpath) return TRUE; } +/** + * Creates a temporary file with given content. + * + * Upon success, and if file is non-NULL, the actual file path used is + * returned in file. This string should be freed with g_free() when not + * needed any longer. + */ +gboolean util_create_tmp_file(const char *content, char **file) +{ + int fp; + ssize_t bytes, len; + + fp = g_file_open_tmp(PROJECT "-XXXXXX", file, NULL); + if (fp == -1) { + g_critical("Could not create temp file %s", *file); + g_free(*file); + return false; + } + + len = strlen(content); + + /* write content into temporary file */ + bytes = write(fp, content, len); + if (bytes < len) { + close(fp); + unlink(*file); + g_critical("Could not write temp file %s", *file); + g_free(*file); + + return false; + } + close(fp); + + return true; +} + /** * Expand ~user, ~/, $ENV and ${ENV} for given string into new allocated * string. @@ -717,7 +753,7 @@ next: * Replaces appearances of search in string by given replace. * Returns a new allocated string if search was found. */ -char *util_str_replace(const char* search, const char* replace, const char* string) +char *util_str_replace(const char *search, const char *replace, const char *string) { if (!string) { return NULL; diff --git a/src/util.h b/src/util.h index 801654d..9043eb2 100644 --- a/src/util.h +++ b/src/util.h @@ -33,6 +33,7 @@ typedef void *(*Util_Content_Func)(const char*, const char*); char *util_build_path(Client *c, const char *path, const char *dir); void util_cleanup(void); gboolean util_create_dir_if_not_exists(const char *dirpath); +gboolean util_create_tmp_file(const char *content, char **file); char *util_expand(Client *c, const char *src, int expflags); gboolean util_file_append(const char *file, const char *format, ...); gboolean util_file_prepend(const char *file, const char *format, ...);