From f2e0c492bd784a3a927550187e21cfbc6bbb0939 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bert=20M=C3=BCnnich?= <ber.t@posteo.de>
Date: Thu, 2 Jan 2014 23:19:31 +0100
Subject: [PATCH] Moved external shell commands into exec/key-handler script

Gets called on all unset key mappings. Arguments are: key combo and
current file. Thanks to Francesco Orsenigo (xarvh) for the idea.
---
 Makefile                      |  4 +-
 commands.c                    | 66 -------------------------
 commands.h                    |  2 -
 config.def.h                  | 17 -------
 image-info => exec/image-info |  0
 exec/key-handler              | 19 ++++++++
 main.c                        | 90 ++++++++++++++++++++++++++++++-----
 7 files changed, 98 insertions(+), 100 deletions(-)
 rename image-info => exec/image-info (100%)
 create mode 100644 exec/key-handler

diff --git a/Makefile b/Makefile
index 17dccb4..cf611c3 100644
--- a/Makefile
+++ b/Makefile
@@ -35,8 +35,8 @@ install: all
 	sed "s!PREFIX!$(PREFIX)!g; s!VERSION!$(VERSION)!g" sxiv.1 > $(DESTDIR)$(MANPREFIX)/man1/sxiv.1
 	chmod 644 $(DESTDIR)$(MANPREFIX)/man1/sxiv.1
 	mkdir -p $(DESTDIR)$(PREFIX)/share/sxiv/exec
-	cp image-info $(DESTDIR)$(PREFIX)/share/sxiv/exec/image-info
-	chmod 755 $(DESTDIR)$(PREFIX)/share/sxiv/exec/image-info
+	cp exec/* $(DESTDIR)$(PREFIX)/share/sxiv/exec/
+	chmod 755 $(DESTDIR)$(PREFIX)/share/sxiv/exec/*
 
 uninstall:
 	rm -f $(DESTDIR)$(PREFIX)/bin/sxiv
diff --git a/commands.c b/commands.c
index 49298bf..9c3d7bf 100644
--- a/commands.c
+++ b/commands.c
@@ -504,69 +504,3 @@ bool it_toggle_alpha(arg_t a)
 	return true;
 }
 
-bool it_open_with(arg_t a)
-{
-	const char *prog = (const char*) a;
-	pid_t pid;
-
-	if (prog == NULL || *prog == '\0')
-		return false;
-
-	if ((pid = fork()) == 0) {
-		execlp(prog, prog,
-		       files[mode == MODE_IMAGE ? fileidx : tns.sel].path, NULL);
-		warn("could not exec: %s", prog);
-		exit(EXIT_FAILURE);
-	} else if (pid < 0) {
-		warn("could not fork. program was: %s", prog);
-	}
-	return false;
-}
-
-bool it_shell_cmd(arg_t a)
-{
-	int n, status;
-	const char *cmdline = (const char*) a;
-	pid_t pid;
-
-	if (cmdline == NULL || *cmdline == '\0')
-		return false;
-
-	n = mode == MODE_IMAGE ? fileidx : tns.sel;
-
-	if (setenv("SXIV_IMG", files[n].path, 1) < 0) {
-		warn("could not set env.-variable: SXIV_IMG. command line was: %s",
-		     cmdline);
-		return false;
-	}
-
-	if ((pid = fork()) == 0) {
-		execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
-		warn("could not exec: /bin/sh. command line was: %s", cmdline);
-		exit(EXIT_FAILURE);
-	} else if (pid < 0) {
-		warn("could not fork. command line was: %s", cmdline);
-		return false;
-	}
-
-	win_set_cursor(&win, CURSOR_WATCH);
-
-	waitpid(pid, &status, 0);
-	if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0)
-		warn("child exited with non-zero return value: %d. command line was: %s",
-		     WEXITSTATUS(status), cmdline);
-	
-	if (mode == MODE_IMAGE) {
-		img_close(&img, true);
-		load_image(fileidx);
-	}
-	if (!tns_load(&tns, n, &files[n], true, mode == MODE_IMAGE) &&
-	    mode == MODE_THUMB)
-	{
-		remove_file(tns.sel, false);
-		tns.dirty = true;
-		if (tns.sel >= tns.cnt)
-			tns.sel = tns.cnt - 1;
-	}
-	return true;
-}
diff --git a/commands.h b/commands.h
index 65c84e7..1002d92 100644
--- a/commands.h
+++ b/commands.h
@@ -69,7 +69,5 @@ bool i_flip(arg_t);
 bool i_toggle_antialias(arg_t);
 bool i_change_gamma(arg_t);
 bool it_toggle_alpha(arg_t);
-bool it_open_with(arg_t);
-bool it_shell_cmd(arg_t);
 
 #endif /* COMMANDS_H */
diff --git a/config.def.h b/config.def.h
index 6a0f1dc..86ba0f5 100644
--- a/config.def.h
+++ b/config.def.h
@@ -151,23 +151,6 @@ static const keymap_t keys[] = {
 	{ 0,            XK_braceleft,     i_change_gamma,       (arg_t) -1 },
 	{ 0,            XK_braceright,    i_change_gamma,       (arg_t) +1 },
 	{ ControlMask,  XK_G,             i_change_gamma,       (arg_t)  0 },
-
-	/* open current image with given program: */
-	{ ControlMask,  XK_g,             it_open_with,         (arg_t) "gimp" },
-
-	/* run shell command line on current file ("$SXIV_IMG"): */
-	{ ControlMask,  XK_less,          it_shell_cmd,         (arg_t) \
-			"mogrify -rotate -90 \"$SXIV_IMG\"" },
-	{ ControlMask,  XK_greater,       it_shell_cmd,         (arg_t) \
-			"mogrify -rotate +90 \"$SXIV_IMG\"" },
-	{ ControlMask,  XK_question,      it_shell_cmd,         (arg_t) \
-			"mogrify -rotate 180 \"$SXIV_IMG\"" },
-	{ ControlMask,  XK_comma,         it_shell_cmd,         (arg_t) \
-			"jpegtran -rotate 270 -copy all -outfile \"$SXIV_IMG\" \"$SXIV_IMG\"" },
-	{ ControlMask,  XK_period,        it_shell_cmd,         (arg_t) \
-			"jpegtran -rotate  90 -copy all -outfile \"$SXIV_IMG\" \"$SXIV_IMG\"" },
-	{ ControlMask,  XK_slash,         it_shell_cmd,         (arg_t) \
-			"jpegtran -rotate 180 -copy all -outfile \"$SXIV_IMG\" \"$SXIV_IMG\"" },
 };
 
 /* mouse button mappings for image mode: */
diff --git a/image-info b/exec/image-info
similarity index 100%
rename from image-info
rename to exec/image-info
diff --git a/exec/key-handler b/exec/key-handler
new file mode 100644
index 0000000..d2e8aa2
--- /dev/null
+++ b/exec/key-handler
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+case "$1" in
+"C-g")
+	gimp "$2" & ;;
+"C-comma")
+	exec jpegtran -rotate 270 -copy all -outfile "$2" "$2" ;;
+"C-period")
+	exec jpegtran -rotate  90 -copy all -outfile "$2" "$2" ;;
+"C-slash")
+	exec jpegtran -rotate 180 -copy all -outfile "$2" "$2" ;;
+"C-less")
+	exec mogrify -rotate -90 "$2" ;;
+"C-greater")
+	exec mogrify -rotate +90 "$2" ;;
+"C-question")
+	exec mogrify -rotate 180 "$2" ;;
+esac
+
diff --git a/main.c b/main.c
index f89a064..5789ff0 100644
--- a/main.c
+++ b/main.c
@@ -46,6 +46,18 @@ enum {
 	TITLE_LEN    = 256
 };
 
+#define EXEC_REL_DIR ".sxiv/exec"
+
+enum {
+	EXEC_INFO,
+	EXEC_KEY
+};
+
+typedef struct {
+	const char *name;
+	char *cmd;
+} exec_t;
+
 typedef struct {
 	struct timeval when;
 	bool active;
@@ -71,9 +83,13 @@ int prefix;
 
 bool resized = false;
 
-const char * const INFO_SCRIPT = ".sxiv/exec/image-info";
+exec_t exec[] = {
+	{ "image-info",  NULL },
+	{ "key-handler", NULL }
+};
+
 struct {
-  char *script;
+  char *cmd;
   int fd;
   unsigned int i, lastsep;
   bool open;
@@ -221,7 +237,7 @@ void open_info(void)
 	static pid_t pid;
 	int pfd[2];
 
-	if (info.script == NULL || info.open || win.bar.h == 0)
+	if (info.cmd == NULL || info.open || win.bar.h == 0)
 		return;
 	if (info.fd != -1) {
 		close(info.fd);
@@ -242,8 +258,8 @@ void open_info(void)
 	} else if (pid == 0) {
 		close(pfd[0]);
 		dup2(pfd[1], 1);
-		execl(info.script, info.script, files[fileidx].name, NULL);
-		warn("could not exec: %s", info.script);
+		execl(info.cmd, info.cmd, files[fileidx].name, NULL);
+		warn("could not exec: %s", info.cmd);
 		exit(EXIT_FAILURE);
 	}
 }
@@ -356,7 +372,7 @@ void update_info(void)
 			              fn, img.multi.sel + 1, img.multi.cnt);
 		}
 		n += snprintf(rt + n, rlen - n, "%0*d/%d", fw, sel + 1, filecnt);
-		ow_info = info.script == NULL;
+		ow_info = info.cmd == NULL;
 	}
 	if (ow_info) {
 		fn = strlen(files[sel].name);
@@ -418,6 +434,49 @@ void clear_resize(void)
 	resized = false;
 }
 
+void key_handler(const char *key, unsigned int mask)
+{
+	pid_t pid;
+	int retval, status, n = mode == MODE_IMAGE ? fileidx : tns.sel;
+	char *cmd = exec[EXEC_KEY].cmd, kstr[32];
+
+	if (cmd == NULL || key == NULL)
+		return;
+
+	snprintf(kstr, sizeof(kstr), "%s%s%s%s",
+	         mask & ControlMask ? "C-" : "",
+	         mask & Mod1Mask    ? "M-" : "",
+	         mask & ShiftMask   ? "S-" : "", key);
+
+	if ((pid = fork()) == 0) {
+		execl(cmd, cmd, kstr, files[n].path, NULL);
+		warn("could not exec key handler");
+		exit(EXIT_FAILURE);
+	} else if (pid < 0) {
+		warn("could not fork key handler");
+		return;
+	}
+	win_set_cursor(&win, CURSOR_WATCH);
+
+	waitpid(pid, &status, 0);
+	retval = WEXITSTATUS(status);
+	if (WIFEXITED(status) == 0 || retval != 0)
+		warn("key handler exited with non-zero return value: %d", retval);
+	if (mode == MODE_IMAGE) {
+		img_close(&img, true);
+		load_image(fileidx);
+	}
+	if (!tns_load(&tns, n, &files[n], true, mode == MODE_IMAGE) &&
+	    mode == MODE_THUMB)
+	{
+		remove_file(tns.sel, false);
+		tns.dirty = true;
+		if (tns.sel >= tns.cnt)
+			tns.sel = tns.cnt - 1;
+	}
+	redraw();
+}
+
 #define MODMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask))
 
 void on_keypress(XKeyEvent *kev)
@@ -438,6 +497,9 @@ void on_keypress(XKeyEvent *kev)
 	XLookupString(kev, &key, 1, &ksym, NULL);
 	sh = (kev->state & ShiftMask) && ksym != shksym ? ShiftMask : 0;
 
+	if (IsModifierKey(ksym))
+		return;
+
 	if ((ksym == XK_Escape && MODMASK(kev->state) == 0) ||
 		  (key >= '0' && key <= '9'))
 	{
@@ -456,6 +518,7 @@ void on_keypress(XKeyEvent *kev)
 			return;
 		}
 	}
+	key_handler(XKeysymToString(ksym), kev->state & ~sh);
 }
 
 void on_buttonpress(XButtonEvent *bev)
@@ -706,16 +769,17 @@ int main(int argc, char **argv)
 
 	if ((homedir = getenv("HOME")) == NULL) {
 		warn("could not locate home directory");
-	} else {
-		len = strlen(homedir) + strlen(INFO_SCRIPT) + 2;
-		info.script = (char*) s_malloc(len);
-		snprintf(info.script, len, "%s/%s", homedir, INFO_SCRIPT);
-		if (access(info.script, X_OK) != 0) {
-			free(info.script);
-			info.script = NULL;
+	} else for (i = 0; i < ARRLEN(exec); i++) {
+		len = strlen(homedir) + strlen(EXEC_REL_DIR) + strlen(exec[i].name) + 3;
+		exec[i].cmd = (char*) s_malloc(len);
+		snprintf(exec[i].cmd, len, "%s/%s/%s", homedir, EXEC_REL_DIR, exec[i].name);
+		if (access(exec[i].cmd, X_OK) != 0) {
+			free(exec[i].cmd);
+			exec[i].cmd = NULL;
 		}
 	}
 	info.fd = -1;
+	info.cmd = exec[EXEC_INFO].cmd;
 
 	if (options->thumb_mode) {
 		mode = MODE_THUMB;