tmenu

Terminal menu for selecting items from stdin
git clone https://git.sinitax.com/sinitax/tmenu
Log | Files | Refs | README | LICENSE | sfeed.txt

commit 3e622e86c7ebbedd97b0d855cfff4ae9b59cc312
parent 10159ed5d2665304cd03cd9b657c7df41296a2cc
Author: Louis Burda <quent.burda@gmail.com>
Date:   Tue, 20 Jul 2021 00:26:45 +0200

Allow output of multiple entries

Diffstat:
Mtmenu.c | 177++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 98 insertions(+), 79 deletions(-)

diff --git a/tmenu.c b/tmenu.c @@ -24,14 +24,22 @@ enum { void* checkp(void *p); void die(const char *fmtstr, ...); -char* aprintf(const char *fmtstr, ...); -ssize_t fgetln(char *buf, size_t size, FILE *f); +char* aprintf(const char *fmtstr, ...); /* yet unused */ -int handleopt(const char *flag, const char **args); +int readkey(FILE *f); +ssize_t freadln(char *buf, size_t size, FILE *f); + +size_t entlen(size_t i); +char* readent(char *buf, size_t linei); +void setent(size_t linei, size_t start); void search_handlekey(int c); void search_selnext(); +int run(); +int handleopt(const char *flag, const char **args); +int main(int argc, const char **argv); + const char *CSI_CLEARLINE = "\x1b[K"; const char *CSI_HIDECUR = "\x1b[25l"; const char *CSI_SHOWCUR = "\x1b[25h"; @@ -45,12 +53,13 @@ const char *userfile = NULL; FILE *f; -size_t linesel, linecap, linec, *lines = NULL; +size_t entsel, entcap, entcnt, *entries = NULL; char searchbuf[1024] = { 0 }; size_t searchc = 0; int mode = MODE_BROWSE; +int multiout = 0; void* checkp(void *p) @@ -95,10 +104,11 @@ aprintf(const char *fmtstr, ...) } int -readcode(FILE *f) +readkey(FILE *f) { int c; + if ((c = fgetc(f)) != '\x1b') return c; if ((c = fgetc(f)) != '[') return c; switch ((c = fgetc(f))) { case 'A': @@ -114,71 +124,85 @@ readcode(FILE *f) return c; } -void -setent(size_t linei, size_t pos) +ssize_t +freadln(char *buf, size_t size, FILE *f) { - if (linei >= linecap) { - linecap *= 2; - lines = checkp(realloc(lines, linecap)); - } - lines[linei] = pos; -} + size_t i = 0; -void -search_handlekey(int c) -{ - switch (c) { - case 127: /* DEL */ - if (searchc) searchc--; - return; - default: - if (searchc < sizeof(searchbuf)) - searchbuf[searchc++] = c & 0xff; - } - search_selnext(); + for (i = 0; i < size && (buf[i] = fgetc(f)) >= 0; i++) + if (buf[i] == '\n') return i + 1; + + return (buf[i] < 0) ? -1 : i; } size_t -linesize(size_t linei) +entlen(size_t linei) { - return lines[linei + 1] - lines[linei]; + return entries[linei + 1] - entries[linei]; } char* -readline(char *buf, int linei) +readent(char *buf, size_t linei) { size_t nleft, nread; char *bp, *tok; - fseek(f, lines[linei], SEEK_SET); - nleft = linesize(linei); + fseek(f, entries[linei], SEEK_SET); + nleft = entlen(linei); buf = bp = checkp(realloc(buf, nleft)); while (nleft && (nread = fread(bp, 1, nleft, f)) > 0) { nleft -= nread; bp += nread; } if (nread < 0) die("Failed to read line %i from input\n", linei); - tok = memchr(buf, '\n', linesize(linei)); + tok = memchr(buf, '\n', entlen(linei)); if (tok) *tok = '\0'; return buf; } void +setent(size_t linei, size_t pos) +{ + if (linei >= entcap) { + entcap *= 2; + entries = checkp(realloc(entries, entcap)); + } + entries[linei] = pos; +} + +void +search_handlekey(int c) +{ + switch (c) { + case 127: /* DEL */ + if (searchc) searchc--; + return; + default: + if (searchc < sizeof(searchbuf)) + searchbuf[searchc++] = c & 0xff; + } + search_selnext(); +} + +void search_selnext() { size_t i, linei, nread, nleft; char *line = NULL, *end, *bp; - if (!searchc) return; + if (!searchc) { + entsel = (entsel + 1) % entcnt; + return; + } - for (i = 0; i < linec; i++) { - linei = (linesel + 1 + i) % linec; - line = bp = readline(line, linei); - end = line + linesize(linei); + for (i = 0; i < entcnt; i++) { + linei = (entsel + 1 + i) % entcnt; + line = bp = readent(line, linei); + end = line + entlen(linei); while (end - bp && (bp = memchr(bp, searchbuf[0], end - bp))) { if (!memcmp(bp, searchbuf, MIN(end - bp, searchc))) { - linesel = linei; + entsel = linei; goto exit; } bp += 1; @@ -189,7 +213,7 @@ exit: free(line); } -void +int run() { struct termios prevterm, newterm; @@ -199,98 +223,97 @@ run() char *tok, iobuf[1024], *line = NULL; int c, termw; - linecap = 100; - lines = checkp(calloc(linecap, sizeof(size_t))); + entcap = 100; + entries = checkp(calloc(entcap, sizeof(size_t))); if (!userfile) { if (!(f = tmpfile())) die("Failed to create temporary file\n"); - linec = start = pos = 0; - while ((nread = fgetln(iobuf, sizeof(iobuf), stdin)) > 0) { + entcnt = start = pos = 0; + while ((nread = freadln(iobuf, sizeof(iobuf), stdin)) > 0) { pos += nread; if (fwrite(iobuf, 1, nread, f) != nread) die("Writing to tmp file failed\n"); if (iobuf[nread-1] == '\n') { - setent(linec++, start); + setent(entcnt++, start); start = pos; } } - setent(linec, pos); + setent(entcnt, pos); fseek(f, 0, SEEK_SET); if (!freopen("/dev/tty", "r", stdin)) die("Failed to reattach to pseudo tty\n"); + if (fread(NULL, 0, 0, f) < 0) return EXIT_FAILURE; } else { if (!(f = fopen(userfile, "r"))) die("Failed to open file for reading: %s\n", userfile); - linec = start = pos = 0; - while ((nread = fgetln(iobuf, sizeof(iobuf), f)) > 0) { + entcnt = start = pos = 0; + while ((nread = freadln(iobuf, sizeof(iobuf), f)) > 0) { pos += nread; if (iobuf[nread-1] == '\n') { - setent(linec++, start); + setent(entcnt++, start); start = pos; } } - setent(linec, pos); + setent(entcnt, pos); fseek(f, 0, SEEK_SET); } - if (!linec) return; + if (!entcnt) return EXIT_SUCCESS; if (tcgetattr(fileno(stdin), &prevterm)) - die("Failed to get terminal properies\n"); + die("Failed to get term attrs\n"); cfmakeraw(&newterm); if (tcsetattr(fileno(stdin), TCSANOW, &newterm)) - die("Failed to set new terminal properties\n"); + die("Failed to set term attrs\n"); termw = (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) != -1) ? ws.ws_col : 80; fprintf(stderr, "%s", CSI_HIDECUR); - linesel = 0; + entsel = 0; do { if (mode == MODE_BROWSE) { - line = readline(line, linesel); + line = readent(line, entsel); fprintf(stderr, "%s%s%.*s\r", CSI_CLEARLINE, prompts[mode], termw - (int) strlen(prompts[mode]), line); } else if (mode == MODE_SEARCH) { - line = readline(line, linesel); + line = readent(line, entsel); fprintf(stderr, "%s%s%.*s: %.*s\r", CSI_CLEARLINE, prompts[mode], (int) searchc, searchbuf, (int) (termw - strlen(prompts[mode]) - searchc), line); } - c = getc(stdin); - if (c == 0x1b) c = readcode(stdin); - switch (c) { + switch ((c = readkey(stdin))) { case 0x03: /* CTRL+C */ goto exit; case 0x13: /* CTRL+S */ - if (mode != MODE_SEARCH) { - mode = MODE_SEARCH; - search_selnext(); - } + mode = MODE_SEARCH; + search_selnext(); break; case 0x02: /* CTRL+B */ mode = MODE_BROWSE; break; - case '\r': - line = readline(line, linesel); - printf("%.*s", (int) linesize(linesel), line); - goto exit; case CODE_UP: - if (linesel != 0) linesel--; + mode = MODE_BROWSE; + if (entsel != 0) entsel--; break; case CODE_DOWN: - if (linesel != linec - 1) linesel++; + mode = MODE_BROWSE; + if (entsel != entcnt - 1) entsel++; break; + case '\r': /* NEWLINE */ + line = readent(line, entsel); + printf("%.*s\n", (int) entlen(entsel), line); + if (!multiout) goto exit; default: if (mode == MODE_SEARCH) { search_handlekey(c); @@ -305,17 +328,8 @@ exit: tcsetattr(fileno(stdin), TCSANOW, &prevterm); fclose(f); -} - -ssize_t -fgetln(char *buf, size_t size, FILE *f) -{ - size_t i = 0; - - for (i = 0; i < size && (buf[i] = fgetc(f)) >= 0; i++) - if (buf[i] == '\n') return i + 1; - return (buf[i] < 0) ? -1 : i; + return EXIT_SUCCESS; } int @@ -323,6 +337,12 @@ handleopt(const char *flag, const char **args) { if (flag[0] && flag[1]) die("Unsupported flag: -%s\n", flag); + switch (flag[0]) { + case 'm': + multiout = 1; + break; + } + return 0; } @@ -341,6 +361,5 @@ main(int argc, const char **argv) } } - run(); + return run(); } -