commit 00593807c8609425c47bc9052360bb945a4327b1
parent dcbd58ec1f3bae1d5bbeccbdb309853564f3392b
Author: Louis Burda <quent.burda@gmail.com>
Date: Sun, 3 Mar 2024 06:34:14 +0100
Add external hook for custom keybinds
Diffstat:
M | tquery.c | | | 318 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
1 file changed, 176 insertions(+), 142 deletions(-)
diff --git a/tquery.c b/tquery.c
@@ -2,6 +2,7 @@
#include <sys/wait.h>
#include <sys/poll.h>
+#include <sys/fcntl.h>
#include <limits.h>
#include <signal.h>
#include <dirent.h>
@@ -25,8 +26,21 @@ struct nav {
ssize_t sel, min, max;
};
+static void sigint_handler(int sig);
+
+static const struct sigaction sigint_action = {
+ .sa_flags = SA_RESTART,
+ .sa_handler = sigint_handler
+};
+
static int child_fd = -1;
static pid_t child_pid = 0;
+static const char **child_argv = NULL;
+
+static pid_t hook_pid = -1;
+static char hook_key_arg[6] = { 0 };
+static char *hook_argv[4] = { NULL, hook_key_arg, NULL, NULL };
+static bool hook_io = false;
static char *loadbuf = NULL;
@@ -43,13 +57,11 @@ static struct nav output_nav;
static char *inputbuf = NULL;
static size_t inputlen = 0;
-static bool splitargs = false;
-static bool single = false;
-static bool childerr = false;
+static bool split_args = false;
+static bool oneshot = false;
+static bool errlog = false;
static char delim = '\n';
-static const char **child_argv = NULL;
-
static void
die(const char *fmt, ...)
{
@@ -72,19 +84,6 @@ die(const char *fmt, ...)
}
static void
-sigint(int sig)
-{
- if (child_pid > 0) {
- kill(child_pid, SIGKILL);
- waitpid(child_pid, NULL, 0);
- child_pid = 0;
- signal(SIGINT, sigint);
- } else {
- exit(0);
- }
-}
-
-static void
nav_update_win(struct nav *nav)
{
nav->wmin = MAX(nav->wmax - nav->wlen, nav->min);
@@ -131,118 +130,37 @@ nav_update_sel(struct nav *nav, ssize_t sel)
}
}
-static char *
-findbin(const char *name)
-{
- static char buf[PATH_MAX];
- char *path, *tok, *ntok, *end;
- struct dirent *ent;
- DIR *dir;
-
- path = getenv("PATH");
- if (!path) return NULL;
-
- ntok = tok = path;
- while (ntok) {
- ntok = strchr(tok, ':');
- end = ntok ? ntok : tok + strlen(tok);
- strncpy(buf, tok, (size_t) (end - tok));
- buf[end - tok] = '\0';
- tok = ntok + 1;
-
- dir = opendir(buf);
- if (!dir) continue;
-
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, name)) {
- closedir(dir);
- if (strlen(buf) + 1 + strlen(name) >= PATH_MAX)
- return NULL;
- strcat(buf, "/");
- strcat(buf, name);
- return buf;
- }
- }
-
- closedir(dir);
- }
-
- return NULL;
-}
-
static void
-spawn(const char **prefix, const char *query)
+invoke(const char **argv, pid_t *pid, int *fd, bool in, bool out, bool err)
{
- static const char *argv[64];
- const char **arg;
- const char *tok, *ntok, *end;
int out_pipe[2];
- size_t argc;
+ int zfd;
- delims_len = 0;
- output_len = 0;
-
- if (child_fd >= 0) {
- close(child_fd);
- child_fd = -1;
- }
-
- if (child_pid > 0) {
- kill(child_pid, SIGKILL);
- waitpid(child_pid, NULL, 0);
- child_pid = 0;
+ if (fd && *fd >= 0) {
+ close(*fd);
+ *fd = -1;
}
if (pipe(out_pipe)) die("pipe:");
- child_pid = fork();
- if (child_pid < 0) die("fork:");
- if (!child_pid) {
- close(0);
- if (childerr) {
- close(1);
- } else {
- close(1);
- close(2);
- }
- dup2(out_pipe[1], 1);
- close(out_pipe[0]);
-
- argc = 0;
- for (arg = prefix; *arg && argc < 64; arg++) {
- if (!strcmp(*arg, "{}")) {
- arg += 1;
- break;
- }
- argv[argc++] = *arg;
- }
-
- if (splitargs) {
- ntok = tok = query;
- while (ntok && *ntok && argc < 64) {
- ntok = strchr(tok, ' ');
- end = ntok ? ntok : tok + strlen(tok);
- argv[argc++] = strndup(tok, (size_t) (end - tok));
- tok = ntok + 1;
- }
- } else if (argc < 64) {
- argv[argc++] = query;
- }
-
- for (; *arg && argc < 64; arg++) {
- argv[argc++] = *arg;
- }
-
- if (argc < 64) argv[argc++] = NULL;
-
- if (argv[argc-1]) die("Too many arguments");
-
- execv(argv[0], (char *const *) argv);
+ *pid = fork();
+ if (*pid < 0) die("fork:");
+ if (!*pid) {
+ zfd = open("/dev/null", O_RDWR);
+ if (zfd < 0) die("open /dev/null");
+ if (!in) dup2(zfd, 0);
+ if (out && fd) dup2(out_pipe[1], 1);
+ else if (!out) dup2(zfd, 1);
+ if (!err) dup2(zfd, 2);
+ if (fd) close(out_pipe[0]);
+ close(zfd);
+ execvp(argv[0], (char *const *) argv);
die("execv '%s':");
} else {
- close(out_pipe[1]);
-
- child_fd = out_pipe[0];
+ if (fd) {
+ close(out_pipe[1]);
+ *fd = out_pipe[0];
+ }
}
}
@@ -291,6 +209,23 @@ load(void)
output_len += (size_t) nread;
}
+static void
+unload(void)
+{
+ int status;
+
+ if (!child_pid) return;
+
+ kill(child_pid, SIGKILL);
+ do {
+ waitpid(child_pid, &status, 0);
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+
+ close(child_fd);
+ child_fd = -1;
+ child_pid = 0;
+}
+
static char *
entry(size_t i)
{
@@ -335,6 +270,83 @@ update(void)
}
static void
+spawn(const char *query)
+{
+ static const char *argv[64];
+ const char **arg;
+ const char *tok, *ntok, *end;
+ size_t argc;
+
+ delims_len = 0;
+ output_len = 0;
+
+ argc = 0;
+ for (arg = child_argv; *arg && argc < 64; arg++) {
+ if (!strcmp(*arg, "{}")) {
+ arg += 1;
+ break;
+ }
+ argv[argc++] = *arg;
+ }
+
+ if (split_args) {
+ ntok = tok = query;
+ while (ntok && *ntok && argc < 64) {
+ ntok = strchr(tok, ' ');
+ end = ntok ? ntok : tok + strlen(tok);
+ argv[argc++] = strndup(tok, (size_t) (end - tok));
+ tok = ntok + 1;
+ }
+ } else if (argc < 64 && query && *query) {
+ argv[argc++] = query;
+ }
+
+ for (; *arg && argc < 64; arg++) {
+ argv[argc++] = *arg;
+ }
+
+ if (argc < 64) {
+ argv[argc++] = NULL;
+ } else {
+ die("Too many arguments");
+ }
+
+ if (child_pid) unload();
+ invoke(argv, &child_pid, &child_fd, false, true, errlog);
+}
+
+static void
+hook(uint8_t key, const char *line)
+{
+ int status, rc;
+
+ if (!hook_argv[0]) return;
+
+ snprintf(hook_argv[1], 6, "%u", key);
+ free(hook_argv[2]);
+ hook_argv[2] = strdup(line);
+
+ if (hook_io) {
+ def_prog_mode();
+ endwin();
+ }
+
+ invoke((const char **)hook_argv, &hook_pid, NULL, hook_io, hook_io, errlog);
+ do {
+ rc = waitpid(hook_pid, &status, 0);
+ if (rc != hook_pid) die("waitpid:");
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+
+ if (hook_io) {
+ reset_prog_mode();
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 3)
+ spawn(inputbuf);
+ update();
+}
+
+static void
input(void)
{
bool dirty;
@@ -394,10 +406,17 @@ input(void)
nav_update_sel(&output_nav, output_nav.max-1);
dirty = true;
break;
+ case CTRL('x'):
+ if (output_nav.max > 0 && output_nav.sel >= 0) {
+ timeout(-1);
+ hook((uint8_t)getch(), entry((size_t)output_nav.sel));
+ timeout(0);
+ }
+ break;
case '\n':
if (output_nav.max > 0 && output_nav.sel >= 0) {
puts(entry((size_t) output_nav.sel));
- if (single) exit(0);
+ if (oneshot) exit(0);
}
break;
default:
@@ -413,8 +432,19 @@ input(void)
}
if (dirty) {
+ if (reload) spawn(inputbuf);
+ update();
+ }
+}
+
+static void
+sigint_handler(int sig)
+{
+ if (child_pid) {
+ unload();
update();
- if (reload) spawn(child_argv, inputbuf);
+ } else {
+ exit(0);
}
}
@@ -426,33 +456,38 @@ usage(int rc, bool full)
fprintf(stderr, "\n");
fprintf(stderr, " -h, --help Show this message\n");
fprintf(stderr, " -d, --delim Set input entry delim\n");
- fprintf(stderr, " -s, --single Exit after single selection\n");
+ fprintf(stderr, " -s, --oneshot Exit after oneshot selection\n");
fprintf(stderr, " -e, --stderr Dont close child stderr\n");
fprintf(stderr, " -a, --args Split the query into args\n");
+ fprintf(stderr, " -x, --hook Program to invoke on Ctrl-x\n");
fprintf(stderr, "\n");
}
exit(rc);
}
static void
-parse(int argc, const char **argv)
+parse(int argc, char **argv)
{
- const char **arg;
- const char *cmd_path;
+ char **arg;
for (arg = &argv[1]; *arg; arg++) {
if (!strcmp(*arg, "-h") || !strcmp(*arg, "--help")) {
usage(0, true);
- } else if (!strcmp(*arg, "-s") || !strcmp(*arg, "--single")) {
- single = true;
+ } else if (!strcmp(*arg, "-o") || !strcmp(*arg, "--oneshot")) {
+ oneshot = true;
} else if (!strcmp(*arg, "-e") || !strcmp(*arg, "--stderr")) {
- childerr = true;
- } else if (!strcmp(*arg, "-a") || !strcmp(*arg, "--args")) {
- splitargs = true;
+ errlog = true;
+ } else if (!strcmp(*arg, "-s") || !strcmp(*arg, "--split")) {
+ split_args = true;
} else if (!strcmp(*arg, "-d") || !strcmp(*arg, "--delim")) {
delim = **++arg;
+ } else if (!strcmp(*arg, "-x") || !strcmp(*arg, "--hook")) {
+ hook_argv[0] = *++arg;
+ } else if (!strcmp(*arg, "-X") || !strcmp(*arg, "--hook-io")) {
+ hook_argv[0] = *++arg;
+ hook_io = true;
} else if (!strcmp(*arg, "--")) {
- child_argv = arg + 1;
+ child_argv = (const char **)arg + 1;
break;
} else {
die("unknown option '%s'", arg[0]);
@@ -461,16 +496,10 @@ parse(int argc, const char **argv)
if (!child_argv || !*child_argv)
usage(0, false);
-
- if (*child_argv[0] != '/' && strncmp(child_argv[0], "./", 2)) {
- cmd_path = findbin(child_argv[0]);
- if (!cmd_path) die("Could not find '%s' in PATH", child_argv[0]);
- child_argv[0] = cmd_path;
- }
}
int
-main(int argc, const char **argv)
+main(int argc, char **argv)
{
struct pollfd fds[2];
int rc;
@@ -479,7 +508,7 @@ main(int argc, const char **argv)
nav_init(&output_nav);
- signal(SIGINT, sigint);
+ sigaction(SIGINT, &sigint_action, NULL);
atexit((void (*)()) endwin);
if (!newterm(NULL, stderr, stdin))
@@ -490,7 +519,7 @@ main(int argc, const char **argv)
timeout(0);
- spawn(child_argv, "");
+ spawn("");
inputbuf = malloc(INPUT_BUFSIZ);
if (!inputbuf) die("malloc inputbuf:");
@@ -513,13 +542,18 @@ main(int argc, const char **argv)
if (rc < 0 && errno == EINTR) {
update();
continue;
- } else if (rc < 0) die("select:");
+ } else if (rc < 0) {
+ die("select:");
+ }
if (fds[0].revents & POLLIN) {
input();
} else if (fds[1].revents & POLLIN) {
load();
update();
+ } else if (fds[1].revents & POLLHUP) {
+ unload();
+ update();
}
}
}