diff --git a/README.md b/README.md
index dd49ddb0..9c5c7bb0 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,7 @@ Stripped binary (or script) size and memory usage of `nnn` and some other simila
1M 50496 15328 4076 S 0.2 vifm
1M 72152 12468 7336 S 0.2 mc
70K 16068 4620 2408 S 0.1 ncdu
- 52K 15712 4368 2512 S 0.1 nnn -S
+ 55K 15712 4368 2512 S 0.1 nnn -S
Intrigued? Find out [HOW](https://github.com/jarun/nnn/wiki/performance-factors).
diff --git a/src/nnn.c b/src/nnn.c
index c6264af5..7c473ab7 100644
--- a/src/nnn.c
+++ b/src/nnn.c
@@ -385,6 +385,7 @@ static char * const utils[] = {
#define STR_INVBM_KEY 3
#define STR_COPY_ID 4
#define STR_DATE_ID 5
+#define STR_UNSAFE 6
static const char messages[][16] = {
"nftw failed",
@@ -393,6 +394,7 @@ static const char messages[][16] = {
"invalid key",
"copy not set",
"%F %T %z",
+ "unsafe cmd",
};
/* Forward declarations */
@@ -879,6 +881,49 @@ static void spawn(const char *file, const char *arg1, const char *arg2, const ch
}
}
+/*
+ * Quotes argument and spawns a shell command
+ * Uses g_buf
+ */
+static bool quote_run_sh_cmd(const char *cmd, const char *arg, const char *path)
+{
+ const char *ptr;
+ size_t r;
+
+ if (!cmd)
+ return FALSE;
+
+ r = xstrlcpy(g_buf, cmd, CMD_LEN_MAX);
+
+ if (arg) {
+ if (r >= CMD_LEN_MAX - 4) { /* space for at least 4 chars - space'c' */
+ printmsg(messages[6]);
+ return FALSE;
+ }
+
+ for (ptr = arg; *ptr; ++ptr)
+ if (*ptr == '\'') {
+ printmsg(messages[6]);
+ return FALSE;
+ }
+
+ g_buf[r - 1] = ' ';
+ g_buf[r] = '\'';
+ r += xstrlcpy(g_buf + r + 1, arg, CMD_LEN_MAX - 1 - r);
+ if (r >= CMD_LEN_MAX - 1) {
+ printmsg(messages[6]);
+ return FALSE;
+ }
+
+ g_buf[r] = '\'';
+ g_buf[r + 1] = '\0';
+ }
+
+ DPRINTF_S(g_buf);
+ spawn("sh", "-c", g_buf, path, F_NORMAL);
+ return TRUE;
+}
+
/* Get program name from env var, else return fallback program */
static char *xgetenv(const char *name, char *fallback)
{
@@ -2717,11 +2762,8 @@ nochange:
if (cfg.useeditor &&
get_output(g_buf, CMD_LEN_MAX, "file", FILE_OPTS, newpath, FALSE) &&
strstr(g_buf, "text/") == g_buf) {
- r = xstrlcpy(g_buf, editor, CMD_LEN_MAX);
- g_buf[r - 1] = ' ';
- xstrlcpy(g_buf + r, newpath, CMD_LEN_MAX - r);
- DPRINTF_S(g_buf);
- spawn("sh", "-c", g_buf, path, F_NORMAL);
+ if (!quote_run_sh_cmd(editor, newpath, path))
+ goto nochange;
continue;
}
@@ -3027,11 +3069,8 @@ nochange:
r = show_help(path);
break;
case SEL_RUNEDIT:
- r = xstrlcpy(g_buf, editor, CMD_LEN_MAX);
- g_buf[r - 1] = ' ';
- xstrlcpy(g_buf + r, dents[cur].name, CMD_LEN_MAX - r);
- r = TRUE;
- spawn("sh", "-c", g_buf, path, F_NORMAL);
+ if (!quote_run_sh_cmd(editor, dents[cur].name, path))
+ goto nochange;
break;
case SEL_RUNPAGE:
r = TRUE;