#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.
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);
+}
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.
* 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;