tquery

Interactive command query tool
git clone https://git.sinitax.com/sinitax/tquery
Log | Files | Refs | LICENSE | sfeed.txt

commit 6cf9c0e99e3f9f2eb3987ec2732f630b55ec680f
parent a7b8ab1c3a951cec9c8ccd2f4db3ec6ef4071ddb
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 29 May 2023 13:07:23 +0200

Remove libdvec dependency and simplify

Diffstat:
M.gitignore | 1+
D.gitmodules | 6------
MMakefile | 17+++++------------
Dbuild.jst | 29-----------------------------
Abuild.jst.tmpl | 34++++++++++++++++++++++++++++++++++
Aconfigure | 3+++
Dlib/liballoc | 1-
Dlib/libdvec | 1-
Mtquery.c | 352+++++++++++++++++++++++++++++++++++++------------------------------------------
9 files changed, 209 insertions(+), 235 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -2,3 +2,4 @@ tquery .cache compile_commands.json .gdb_history +build.jst diff --git a/.gitmodules b/.gitmodules @@ -1,6 +0,0 @@ -[submodule "lib/liballoc"] - path = lib/liballoc - url = git@sinitax.com:sinitax/liballoc -[submodule "lib/libdvec"] - path = lib/libdvec - url = git@sinitax.com:sinitax/libdvec diff --git a/Makefile b/Makefile @@ -1,8 +1,9 @@ PREFIX ?= /usr/local BINDIR ?= /bin -LDLIBS = -lcurses -I lib/libdvec/include -I lib/liballoc/include -CFLAGS = -std=c99 -Wunused-function -Wunused-variable -Wconversion +CFLAGS = -std=c99 -D_XOPEN_SOURCE=700 -Og -g \ + -Wunused-function -Wunused-variable -Wconversion -Wsign-compare +LDLIBS = -lcurses all: tquery @@ -10,16 +11,8 @@ clean: rm -f tquery cleanall: clean - make -C lib/liballoc cleanall - make -C lib/libdvec cleanall -lib/libdvec/build/libdvec.a: - make -C lib/libdvec build/libdvec.a - -lib/liballoc/build/liballoc.a: - make -C lib/liballoc build/liballoc.a - -tquery: tquery.c lib/libdvec/build/libdvec.a lib/liballoc/build/liballoc.a +tquery: tquery.c install: install -m755 tquery -t "$(DESTDIR)$(PREFIX)$(BINDIR)" @@ -27,4 +20,4 @@ install: uninstall: rm -f "$(DESTDIR)$(PREFIX)$(BINDIR)/tquery" -.PHONY: all clean install uninstall +.PHONY: all clean cleanall install uninstall diff --git a/build.jst b/build.jst @@ -1,29 +0,0 @@ -cc = clang - -ldlibs = -lcurses -I lib/libdvec/include -I lib/liballoc/include -cflags = -std=c99 -Wunused-function -Wunused-variable -Wconversion - -rule cc - $cc -o $out $in $cflags $ldlibs - -target lib/liballoc/build/liballoc.a - just lib/liballoc - -target lib/libdvec/build/libdvec.a - just lib/libdvec - -target tquery - cc tquery.c lib/liballoc/build/liballoc.a - lib/libdvec/build/libdvec.a - -command clean - rm -rf build - -command cleanall - just clean - just -C lib/liballoc cleanall - just -C lib/libdvec cleanall - -command all - just tquery - diff --git a/build.jst.tmpl b/build.jst.tmpl @@ -0,0 +1,34 @@ +#default PREFIX /usr/local +#default BINDIR /bin +#default CC gcc + +#ifdef DEBUG +#define OPT_CFLAGS -Og -g +#else +#define OPT_CFLAGS -O2 +#endif + +cflags = -std=c99 -D_XOPEN_SOURCE=700 #{OPT_CFLAGS} #{EXTRA_CFLAGS} + -Wunused-function -Wunused-variable -Wconversion -Wsign-compare +ldlibs = -lcurses + +rule cc + #{CC} -o $out $in $cflags $ldlibs + +target tquery + cc tquery.c + +command clean + rm -rf build + +command cleanall + just clean + +command install + install -m755 tquery -t "#{DESTDIR}#{PREFIX}#{BINDIR}" + +command uninstall + rm -f "#{DESTDIR}#{PREFIX}#{BINDIR}/tquery" + +command all + just tquery diff --git a/configure b/configure @@ -0,0 +1,3 @@ +#!/bin/sh + +tmpl "$@" build.jst.tmpl > build.jst diff --git a/lib/liballoc b/lib/liballoc @@ -1 +0,0 @@ -Subproject commit db114713e3958e350da181c8894ccf330320f66b diff --git a/lib/libdvec b/lib/libdvec @@ -1 +0,0 @@ -Subproject commit 83df399924762dd234c2b34f14e296bd3590c147 diff --git a/tquery.c b/tquery.c @@ -1,104 +1,102 @@ -#define _XOPEN_SOURCE 700 - -#include "allocator.h" -#include "dvec.h" - #include <curses.h> -#include <linux/limits.h> #include <sys/wait.h> #include <sys/select.h> #include <limits.h> -#include <fcntl.h> +#include <signal.h> #include <dirent.h> #include <unistd.h> -#include <termios.h> -#include <signal.h> #include <errno.h> -#include <assert.h> #include <string.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #define CTRL(x) ((x) & 0x1f) + #define MIN(a, b) ((a) > (b) ? (b) : (a)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) -struct listnav { +struct nav { int wmin, wmax, wlen; - int sel; - int min, max; + int sel, min, max; }; -static const struct allocator *ga = &stdlib_strict_heap_allocator; +static int child_fd = -1; +static pid_t child_pid = 0; -static int locate_out_fd = -1; -static pid_t locate_pid = 0; +static char *output = NULL; +static size_t output_len = 0; +static size_t output_cap = 0; -static struct dvec locate_output; -static struct dvec locate_delims; +static size_t *delims = NULL; +static size_t delims_len = 0; +static size_t delims_cap = 0; -static struct listnav locate_nav; +static struct nav output_nav; static char inputbuf[2048] = { 0 }; static size_t inputlen = 0; -static char errbuf[1024] = { 0 }; -static int errtimer = 0; - -static bool separate_args = false; -static bool single_out = false; -static bool debug = false; +static bool separate = false; +static bool oneshot = false; +static bool openerr = false; static char delim = '\n'; -static const char **cmd_argv = NULL; +static const char **child_argv = NULL; static void -sigint(int sig) -{ - if (locate_pid > 0) { - kill(locate_pid, SIGKILL); - waitpid(locate_pid, NULL, 0); - locate_pid = 0; - signal(SIGINT, sigint); - } else { - exit(0); - } -} - -static void __attribute__((noreturn)) -err(const char *fmt, ...) +die(const char *fmt, ...) { va_list ap; - endwin(); + /* stdout because of swapped fds */ + + fputs("tquery: ", stdout); va_start(ap, fmt); - printf("tquery: "); - vprintf(fmt, ap); - printf(": %s\n", strerror(errno)); + vfprintf(stdout, fmt, ap); va_end(ap); + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + exit(1); } +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); + } +} + void swapfd(int fd1, int fd2) { int fd, rc; fd = dup(fd1); - if (fd < 0) err("dup"); + if (fd < 0) die("dup:"); rc = dup2(fd2, fd1); - if (rc == -1) err("dup2"); + if (rc == -1) die("dup2:"); rc = dup2(fd, fd2); - if (rc == -1) err("dup2"); + if (rc == -1) die("dup2:"); close(fd); } static void -listnav_update_win(struct listnav *nav) +nav_update_win(struct nav *nav) { nav->wmin = MAX(nav->wmax - nav->wlen, nav->min); nav->wmax = MIN(nav->wmin + nav->wlen, nav->max); @@ -106,7 +104,7 @@ listnav_update_win(struct listnav *nav) } static void -listnav_init(struct listnav *nav) +nav_init(struct nav *nav) { nav->sel = 0; nav->min = 0; @@ -117,22 +115,22 @@ listnav_init(struct listnav *nav) } static void -listnav_update_bounds(struct listnav *nav, int min, int max) +nav_update_bounds(struct nav *nav, int min, int max) { nav->min = min; nav->max = max; - listnav_update_win(nav); + nav_update_win(nav); } static void -listnav_update_wlen(struct listnav *nav, int wlen) +nav_update_wlen(struct nav *nav, int wlen) { nav->wlen = wlen; - listnav_update_win(nav); + nav_update_win(nav); } static void -listnav_update_sel(struct listnav *nav, int sel) +nav_update_sel(struct nav *nav, int sel) { nav->sel = MAX(MIN(sel, nav->max - 1), nav->min); if (nav->sel >= nav->wmax) { @@ -144,28 +142,10 @@ listnav_update_sel(struct listnav *nav, int sel) } } -void -reset(void) -{ - dvec_clear(&locate_delims); - dvec_clear(&locate_output); - - if (locate_out_fd >= 0) { - close(locate_out_fd); - locate_out_fd = -1; - } - - if (locate_pid > 0) { - kill(locate_pid, SIGKILL); - waitpid(locate_pid, NULL, 0); - locate_pid = 0; - } -} - char * findbin(const char *name) { - char buf[PATH_MAX]; + static char buf[PATH_MAX]; char *path, *tok, *ntok, *end; struct dirent *ent; DIR *dir; @@ -191,7 +171,7 @@ findbin(const char *name) return NULL; strcat(buf, "/"); strcat(buf, name); - return strdup(buf); + return buf; } } @@ -204,18 +184,33 @@ findbin(const char *name) static void spawn(const char **prefix, const char *query) { - struct dvec argv; + static const char *argv[64]; const char **arg; const char *tok, *ntok, *end; int out_pipe[2]; + size_t argc; + + delims_len = 0; + output_len = 0; - if (pipe(out_pipe)) err("pipe"); + if (child_fd >= 0) { + close(child_fd); + child_fd = -1; + } - locate_pid = fork(); - if (locate_pid < 0) err("fork"); - if (!locate_pid) { + if (child_pid > 0) { + kill(child_pid, SIGKILL); + waitpid(child_pid, NULL, 0); + child_pid = 0; + } + + if (pipe(out_pipe)) die("pipe:"); + + child_pid = fork(); + if (child_pid < 0) die("fork:"); + if (!child_pid) { close(0); - if (debug) { + if (openerr) { swapfd(1, 2); close(1); } else { @@ -225,50 +220,57 @@ spawn(const char **prefix, const char *query) dup2(out_pipe[1], 1); close(out_pipe[0]); - dvec_init(&argv, sizeof(char *), 0, ga); - for (arg = prefix; *arg; arg++) { + argc = 0; + for (arg = prefix; *arg && argc < 64; arg++) { if (!strcmp(*arg, "{}")) { arg += 1; break; } - dvec_add_back(&argv, 1); - *(const char **)dvec_back(&argv) = *arg; + argv[argc++] = *arg; } - if (separate_args) { + if (separate) { ntok = tok = query; - while (ntok && *ntok) { + while (ntok && *ntok && argc < 64) { ntok = strchr(tok, ' '); end = ntok ? ntok : tok + strlen(tok); - dvec_add_back(&argv, 1); - *(const char **)dvec_back(&argv) - = strndup(tok, (size_t) (end - tok)); + argv[argc++] = strndup(tok, (size_t) (end - tok)); tok = ntok + 1; } - } else { - dvec_add_back(&argv, 1); - *(const char **)dvec_back(&argv) = query; + } else if (argc < 64) { + argv[argc++] = query; } - for (; *arg; arg++) { - dvec_add_back(&argv, 1); - *(const char **)dvec_back(&argv) = *arg; + for (; *arg && argc < 64; arg++) { + argv[argc++] = *arg; } - dvec_add_back(&argv, 1); - *(const char **)dvec_back(&argv) = NULL; + if (argc < 64) argv[argc++] = NULL; + + if (argv[argc-1]) die("Too many arguments"); - execv(*(const char **)dvec_front(&argv), argv.data); - fprintf(stderr, "tquery: execv '%s': %s", - *(const char **)dvec_front(&argv), strerror(errno)); - exit(1); + execv(argv[0], (char *const *) argv); + die("execv '%s':"); } else { close(out_pipe[1]); - locate_out_fd = out_pipe[0]; + child_fd = out_pipe[0]; } } +static void * +addcap(void *alloc, size_t dsize, size_t min, size_t *cap) +{ + if (min > *cap) { + *cap = *cap * 2; + if (*cap < min) *cap = min; + alloc = realloc(alloc, dsize * *cap); + if (!alloc) die("realloc:"); + } + + return alloc; +} + static void load(void) { @@ -277,41 +279,34 @@ load(void) size_t i, len; char *start; - nread = read(locate_out_fd, buf, BUFSIZ); + nread = read(child_fd, buf, BUFSIZ); if (nread <= 0) { - locate_pid = 0; - close(locate_out_fd); - locate_out_fd = -1; + child_pid = 0; + close(child_fd); + child_fd = -1; return; } - len = dvec_len(&locate_output); - dvec_add_back(&locate_output, (size_t) nread); - start = dvec_at(&locate_output, len); - memcpy(start, buf, (size_t) nread); + output = addcap(output, 1, output_len + (size_t) nread, &output_cap); + memcpy(output + output_len, buf, (size_t) nread); + + start = output + output_len; + output_len += (size_t) nread; for (i = len; i < len + (size_t) nread; i++, start++) { if (*start == delim) { *start = '\0'; - dvec_add_back(&locate_delims, 1); - *(size_t *)dvec_back(&locate_delims) = i; + delims = addcap(delims, sizeof(size_t), + delims_len + 1, &delims_cap); + delims[delims_len++] = i; } } } static char * -entry(int i) +entry(size_t i) { - size_t off; - - assert(i >= 0); - - if (i == 0) { - return dvec_at(&locate_output, 0); - } else { - off = *(size_t *)dvec_at(&locate_delims, (size_t) i - 1); - return dvec_at(&locate_output, off + 1); - } + return output + (i ? delims[i-1] + 1 : 0); } static void @@ -326,30 +321,21 @@ update(void) width = getmaxx(stdscr); height = getmaxy(stdscr); - if (errbuf[0] && errtimer >= 0) { - attr_on(A_BOLD, NULL); - mvprintw(0, 1, "%s", errbuf); - attr_off(A_BOLD, NULL); - errtimer--; - } - - mvprintw(1, 1, "%lu %lu", dvec_len(&locate_output), - dvec_len(&locate_delims)); + mvprintw(1, 1, "%lu %lu %i", output_len, delims_len, child_pid); mvprintw(2, 1, "> %.*s", width - 1, inputbuf); - if (!dvec_empty(&locate_output)) { - listnav_update_bounds(&locate_nav, 0, (int) dvec_len(&locate_delims)); - listnav_update_wlen(&locate_nav, (int) (height - miny)); + if (output_len) { + nav_update_bounds(&output_nav, 0, (int) delims_len); + nav_update_wlen(&output_nav, (int) (height - miny)); - i = locate_nav.wmin; - for (y = miny; y < height && i < locate_nav.wmax; y++, i++) { - if (i == locate_nav.sel) + i = output_nav.wmin; + for (y = miny; y < height && i < output_nav.wmax; y++, i++) { + if (i == output_nav.sel) attr_on(A_REVERSE, NULL); - mvprintw((int) y, 1, "%.*s", - width - 1, entry((int) i)); + mvprintw((int) y, 1, "%.*s", width - 1, entry((size_t) i)); - if (i == locate_nav.sel) + if (i == output_nav.sel) attr_off(A_REVERSE, NULL); } } @@ -376,21 +362,21 @@ input(void) reload = true; break; case KEY_PPAGE: - listnav_update_sel(&locate_nav, - locate_nav.sel - locate_nav.wlen / 2); + nav_update_sel(&output_nav, + output_nav.sel - output_nav.wlen / 2); dirty = true; break; case KEY_NPAGE: - listnav_update_sel(&locate_nav, - locate_nav.sel + locate_nav.wlen / 2); + nav_update_sel(&output_nav, + output_nav.sel + output_nav.wlen / 2); dirty = true; break; case KEY_DOWN: - listnav_update_sel(&locate_nav, locate_nav.sel + 1); + nav_update_sel(&output_nav, output_nav.sel + 1); dirty = true; break; case KEY_UP: - listnav_update_sel(&locate_nav, locate_nav.sel - 1); + nav_update_sel(&output_nav, output_nav.sel - 1); dirty = true; break; case '\x1b': @@ -401,17 +387,21 @@ input(void) case 'd': break; } + break; case CTRL('w'): inputlen = 0; dirty = true; reload = true; break; + case CTRL('l'): + clear(); + dirty = true; + break; case '\n': - if (!isatty(2)) { - fprintf(stderr, "%s\n", entry(locate_nav.sel)); + if (output_nav.sel >= 0) { + fputs(entry((size_t) output_nav.sel), stderr); + if (oneshot) exit(0); } - if (single_out) - exit(0); break; default: if (c < 32 || c >= 127) @@ -427,10 +417,7 @@ input(void) if (dirty) { update(); - if (reload) { - reset(); - spawn(cmd_argv, inputbuf); - } + if (reload) spawn(child_argv, inputbuf); } } @@ -441,10 +428,10 @@ usage(int rc, bool full) if (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, " -d, --debug Enable debug mode\n"); + fprintf(stderr, " -e, --stderr Dont close child stderr\n"); fprintf(stderr, " -a, --args Split the query into args\n"); - fprintf(stderr, " -c, --delim Set input entry delim\n"); fprintf(stderr, "\n"); } exit(rc); @@ -459,34 +446,30 @@ parse(int argc, const char **argv) 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_out = true; - } else if (!strcmp(*arg, "-d") || !strcmp(*arg, "--debug")) { - debug = true; - } else if (!strcmp(*arg, "-a") || !strcmp(*arg, "--args")) { - separate_args = true; - } else if (!strcmp(*arg, "-c") || !strcmp(*arg, "--delim")) { + } else if (!strcmp(*arg, "-o") || !strcmp(*arg, "--oneshot")) { + oneshot = true; + } else if (!strcmp(*arg, "-e") || !strcmp(*arg, "--openerr")) { + openerr = true; + } else if (!strcmp(*arg, "-s") || !strcmp(*arg, "--separate")) { + separate = true; + } else if (!strcmp(*arg, "-d") || !strcmp(*arg, "--delim")) { delim = **++arg; } else if (!strcmp(*arg, "--")) { - cmd_argv = arg + 1; + child_argv = arg + 1; break; } else { - fprintf(stderr, "tquery: unknown option '%s'\n", arg[0]); - exit(1); + die("unknown option '%s'", arg[0]); } } - if (!cmd_argv || !*cmd_argv) { + if (!child_argv || !*child_argv) { usage(0, false); } - if (*cmd_argv[0] != '/' && strncmp(cmd_argv[0], "./", 2)) { - cmd_path = findbin(cmd_argv[0]); - if (!cmd_path) { - fprintf(stderr, "tquery: no '%s' in PATH\n", cmd_argv[0]); - exit(1); - } - cmd_argv[0] = cmd_path; + 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; } } @@ -500,9 +483,7 @@ main(int argc, const char **argv) swapfd(1, 2); - dvec_init(&locate_output, 1, 1024 * 1024 * 25, ga); - dvec_init(&locate_delims, sizeof(size_t), 1024, ga); - listnav_init(&locate_nav); + nav_init(&output_nav); signal(SIGINT, sigint); @@ -516,28 +497,27 @@ main(int argc, const char **argv) update(); - reset(); - spawn(cmd_argv, ""); + spawn(child_argv, ""); inputlen = 0; while (1) { maxfd = 0; FD_ZERO(&set); FD_SET(0, &set); - if (locate_pid) { - FD_SET(locate_out_fd, &set); - if (locate_out_fd > maxfd) - maxfd = locate_out_fd; + if (child_pid) { + FD_SET(child_fd, &set); + if (child_fd > maxfd) + maxfd = child_fd; } rc = select(maxfd+1, &set, NULL, NULL, NULL); if (rc <= 0 && errno == EINTR) { update(); continue; - } else if (rc <= 0) err("select"); + } else if (rc <= 0) die("select:"); if (FD_ISSET(0, &set)) { input(); - } else if (FD_ISSET(locate_out_fd, &set)) { + } else if (FD_ISSET(child_fd, &set)) { load(); update(); }