zygo

ncurses gopher client
Log | Files | Refs

commit b8087842bf2f2da4dfa2b16165f42030f9215181
parent b21bec2bc9fa09163f4ce68cd35152174c79f7e8
Author: hhvn <dev@hhvn.uk>
Date:   Mon, 17 Jan 2022 01:06:32 +0000

zygo.c config.def.h: regex matching

Diffstat:
Mconfig.def.h | 1+
Mzygo.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -2,6 +2,7 @@ static char *starturi = "gopher://hhvn.uk/1/git/o/zygo"; static char *plumber = "xdg-open"; static int parallelplumb = 0; static int stimeout = 5; +static int regexflags = REG_ICASE|REG_EXTENDED; static short bar_pair[2] = {-1, 0}; static short uri_pair[2] = {0, 7}; diff --git a/zygo.c b/zygo.c @@ -26,6 +26,7 @@ #include <locale.h> #include <signal.h> #include <unistd.h> +#include <regex.h> #include <ctype.h> #include <stdio.h> #include <sys/wait.h> @@ -49,7 +50,9 @@ struct { wint_t input[BUFLEN]; char cmd; char *arg; -} ui = {0, 0}; + int search; + regex_t regex; +} ui = {.scroll = 0, .wantinput = 0, .search = 0}; /* * Memory functions @@ -484,7 +487,13 @@ go(Elem *e, int mhist) { list_append(&history, current); elem_free(current); current = dup; + ui.scroll = 0; + if (ui.search) { + regfree(&ui.regex); + ui.search = 0; + } + return 0; } @@ -536,6 +545,57 @@ getscheme(Elem *e) { return &scheme[i]; } +void +find(int backward) { + enum {mfirst, mclose, mlast}; + struct { + size_t pos; + int found; + } matches[] = { + [mfirst] = {.found = 0}, + [mclose] = {.found = 0}, + [mlast] = {.found = 0}, + }; + size_t i; + size_t want; + + if (!ui.search) { + error("no search"); + return; + } + + for (i = 0; i < list_len(&page); i++) { + if (regexec(&ui.regex, list_get(&page, i)->desc, 0, NULL, 0) == 0) { + matches[mlast].found = 1; + matches[mlast].pos = i; + if (!matches[mfirst].found) { + matches[mfirst].found = 1; + matches[mfirst].pos = i; + } + if (!matches[mclose].found && ((backward && i < ui.scroll) || (!backward && i > ui.scroll))) { + matches[mclose].found = 1; + matches[mclose].pos = i; + } + } + } + + if (matches[mfirst].found == 0 && + matches[mclose].found == 0 && + matches[mlast].found == 0) { + error("no match"); + return; + } + + if (matches[mclose].found) + want = matches[mclose].pos; + else if (backward && matches[mlast].found) + want = matches[mlast].pos; + else + want = matches[mfirst].pos; + + ui.scroll = want; +} + int draw_line(Elem *e, int maxlines) { int lc, cc; @@ -549,7 +609,12 @@ draw_line(Elem *e, int maxlines) { attron(COLOR_PAIR(getscheme(e)->pair)); printw("%s ", getscheme(e)->name); attroff(A_COLOR); - printw("| %s\n", e->desc); + printw("| "); + if (ui.search && regexec(&ui.regex, e->desc, 0, NULL, 0) == 0) + attron(A_REVERSE); + printw("%s", e->desc); + attroff(A_REVERSE); + printw("\n"); return 1; } @@ -620,6 +685,7 @@ run(void) { int ret; size_t il; Elem *e; + char tmperror[BUFLEN]; draw_page(); draw_bar(); @@ -657,7 +723,23 @@ run(void) { candraw = 0; } break; + case '/': + case '?': + if (ui.search) { + regfree(&ui.regex); + ui.search = 0; + } + if (ui.input[0] != '\0') { + if ((ret = regcomp(&ui.regex, ui.arg, regexflags)) != 0) { + regerror(ret, &ui.regex, (char *)&tmperror, sizeof(tmperror)); + error("could not compile regex '%s': %s", ui.arg, tmperror); + } else { + ui.search = 1; + find(ui.cmd == '?' ? 1 : 0); + } + } + break; } ui.wantinput = 0; draw_page(); @@ -753,6 +835,11 @@ run(void) { ui.scroll = list_len(&page) - LINES; draw_page(); break; + case 'n': + case 'N': + find(c == 'N' ? 1 : 0); + draw_page(); + break; /* link numbers */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -768,6 +855,7 @@ run(void) { /* commands with arg */ case ':': case '+': + case '/': ui.cmd = (char)c; ui.wantinput = 1; ui.input[0] = '\0';