summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--tmenu.c145
2 files changed, 114 insertions, 32 deletions
diff --git a/Makefile b/Makefile
index 7fdd099..73aa475 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@ PREFIX ?= /usr/local
BINDIR ?= /bin
CFLAGS = -g -Wunused-variable -Wunused-function -Wconversion
+LDLIBS = -lcurses
all: tmenu
diff --git a/tmenu.c b/tmenu.c
index d5a0fd6..e2efca0 100644
--- a/tmenu.c
+++ b/tmenu.c
@@ -1,4 +1,8 @@
+#include <ncurses.h>
+
#include <sys/ioctl.h>
+#include <sys/wait.h>
+
#include <signal.h>
#include <unistd.h>
#include <termios.h>
@@ -51,14 +55,14 @@ enum {
};
enum {
- KEY_NONE = 0,
- KEY_DEL = 0x7f,
- KEY_UP = 0x100,
- KEY_DOWN,
- KEY_LEFT,
- KEY_RIGHT,
- KEY_PGUP,
- KEY_PGDN,
+ TKEY_NONE = 0,
+ TKEY_DEL = 0x7f,
+ TKEY_UP = 0x100,
+ TKEY_DOWN,
+ TKEY_LEFT,
+ TKEY_RIGHT,
+ TKEY_PGUP,
+ TKEY_PGDN,
};
struct mode {
@@ -128,7 +132,6 @@ static size_t entries_cap = 0;
static size_t entries_cnt = 0;
static ssize_t selected = -1;
-static const char *entry = NULL;
static size_t entry_off = 0;
static char searchbuf[1024];
@@ -147,6 +150,10 @@ static bool verbose = false;
static bool show_prompt = true;
static bool top_prompt = false;
+static const char *run_argv[3] = { NULL };
+static bool run_cmd = false;
+static bool run_io = false;
+
static char delim = '\n';
static char vis_delim = '|';
@@ -238,24 +245,24 @@ readkey(FILE *f)
return c;
if (fgetc(f) != '[')
- return KEY_NONE;
+ return TKEY_NONE;
switch (fgetc(f)) {
case 'A':
- return KEY_UP;
+ return TKEY_UP;
case 'B':
- return KEY_DOWN;
+ return TKEY_DOWN;
case 'C':
- return KEY_RIGHT;
+ return TKEY_RIGHT;
case 'D':
- return KEY_LEFT;
+ return TKEY_LEFT;
case '5':
- return fgetc(f) == '~' ? KEY_PGUP : KEY_NONE;
+ return fgetc(f) == '~' ? TKEY_PGUP : TKEY_NONE;
case '6':
- return fgetc(f) == '~' ? KEY_PGDN : KEY_NONE;
+ return fgetc(f) == '~' ? TKEY_PGDN : TKEY_NONE;
}
- return KEY_NONE;
+ return TKEY_NONE;
}
static int
@@ -296,6 +303,7 @@ static void
browse_prompt(void)
{
size_t linew, entlen;
+ const char *entry;
ssize_t i;
if (selected < 0) selected = 0;
@@ -356,25 +364,25 @@ browse_handlekey(int c)
break;
case 'q':
return true;
- case KEY_PGUP:
+ case TKEY_PGUP:
cnt = fwdctx + bwdctx + 1;
if (selected > cnt)
selected -= (ssize_t) cnt;
else
selected = 0;
break;
- case KEY_PGDN:
+ case TKEY_PGDN:
cnt = fwdctx + bwdctx + 1;
if (selected < entries_cnt - cnt)
selected += (ssize_t) cnt;
else
selected = (ssize_t) entries_cnt - 1;
break;
- case KEY_UP:
+ case TKEY_UP:
if (selected != 0)
selected--;
break;
- case KEY_DOWN:
+ case TKEY_DOWN:
if (selected != entries_cnt - 1)
selected++;
break;
@@ -399,6 +407,7 @@ search_prompt(void)
{
size_t linew, off, entlen;
ssize_t i, index;
+ const char *entry;
if (selected < 0) selected = 0;
@@ -480,27 +489,27 @@ search_handlekey(int c)
case KEY_CTRL('I'):
searchcase ^= 1;
break;
- case KEY_PGUP:
+ case TKEY_PGUP:
cnt = fwdctx + bwdctx + 1;
selected = search_match(selected, BWD, cnt, selected, true, true);
break;
- case KEY_PGDN:
+ case TKEY_PGDN:
cnt = fwdctx + bwdctx + 1;
selected = search_match(selected, FWD, cnt, selected, true, true);
break;
case KEY_CTRL('K'):
- case KEY_UP:
+ case TKEY_UP:
selected = search_match(selected, BWD, 1, selected, true, true);
break;
case KEY_CTRL('L'):
- case KEY_DOWN:
+ case TKEY_DOWN:
selected = search_match(selected, FWD, 1, selected, true, true);
break;
case 0x20 ... 0x7e:
if (searchlen < sizeof(searchbuf) - 1)
searchbuf[searchlen++] = (char) (c & 0xff);
break;
- case KEY_DEL:
+ case TKEY_DEL:
if (searchlen) searchlen--;
break;
}
@@ -545,7 +554,7 @@ static ssize_t
search_match_substr(ssize_t start, int dir,
size_t cnt, ssize_t fallback, bool new, bool closest)
{
- const char *end, *bp;
+ const char *end, *bp, *entry;
size_t i, found;
ssize_t index, prev;
@@ -581,7 +590,7 @@ static ssize_t
search_match_fuzzy(ssize_t start, int dir,
size_t cnt, ssize_t fallback, bool new, bool closest)
{
- const char *end, *pos, *c;
+ const char *end, *pos, *c, *entry;
size_t i, found;
ssize_t index, prev;
@@ -666,7 +675,68 @@ load(int fd)
}
static void
-run(void)
+invoke(const char **argv, pid_t *pid, int *fd, bool in, bool out, bool err)
+{
+ int out_pipe[2];
+
+ if (fd) {
+ if (*fd >= 0) {
+ close(*fd);
+ *fd = -1;
+ }
+ if (pipe(out_pipe))
+ die("pipe:");
+ }
+
+ *pid = fork();
+ if (*pid < 0) die("fork:");
+ if (!*pid) {
+ if (!in || !out || !err) {
+ int zfd = open("/dev/null", O_RDWR);
+ if (zfd < 0) die("open /dev/null");
+ if (!in) dup2(zfd, 0);
+ if (!out) dup2(zfd, 1);
+ if (!err) dup2(zfd, 2);
+ close(zfd);
+ }
+ if (fd) {
+ if (out) dup2(out_pipe[1], 1);
+ close(out_pipe[0]);
+ close(out_pipe[1]);
+ }
+ execvp(argv[0], (char *const *) argv);
+ die("execv '%s':", argv[0]);
+ } else {
+ if (fd) {
+ *fd = out_pipe[0];
+ close(out_pipe[1]);
+ }
+ }
+}
+
+static void
+run(char *input)
+{
+ if (run_io) {
+ def_prog_mode();
+ endwin();
+ }
+
+ int status, pid;
+ run_argv[1] = input;
+ invoke((const char **)run_argv, &pid, NULL, run_io, run_io, run_io);
+ do {
+ int rc = waitpid(pid, &status, 0);
+ if (rc != pid) die("waitpid:");
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+
+ if (run_io) {
+ reset_prog_mode();
+ }
+}
+
+static void
+loop(void)
{
struct termios prevterm, newterm = { 0 };
int c;
@@ -716,9 +786,11 @@ run(void)
case KEY_CTRL('J'):
case '\r':
if (selected < 0) break;
- entry = full_entry((size_t) selected);
modes[mode].cleanup();
- puts(entry);
+ if (run_cmd)
+ run(full_entry((size_t) selected));
+ else
+ puts(full_entry((size_t) selected));
if (!multiout) goto exit;
break;
default:
@@ -785,6 +857,15 @@ parseopt(const char *flag, const char **args)
if (args[0][0] && args[0][1]) die("bad -d arg");
vis_delim = **args;
return 1;
+ case 'e':
+ run_argv[0] = *args;
+ run_cmd = true;
+ return 1;
+ case 'E':
+ run_argv[0] = *args;
+ run_cmd = true;
+ run_io = true;
+ return 1;
case 't':
top_prompt = true;
return 0;
@@ -825,5 +906,5 @@ main(int argc, const char **argv)
die("freopen tty:");
}
- run();
+ loop();
}