diff --git a/src/gmnlm.c b/src/gmnlm.c

index b7cc12dbbb391dd3e72e5e30d21a7e7957872f60..5cc2e18a06dc99f2258d6bfc875c2c01ceb265c2 100644

--- a/src/gmnlm.c

+++ b/src/gmnlm.c

@@ -3,6 +3,7 @@ #include <ctype.h>

#include <getopt.h>

#include <openssl/bio.h>

#include <openssl/err.h>

+#include <regex.h>

#include <stdbool.h>

#include <stdio.h>

#include <string.h>

@@ -32,6 +33,9 @@ struct Curl_URL *url;

struct link *links;

struct history *history;

bool running;

+

+ bool searching;

+ regex_t regex;

};

enum prompt_result {

@@ -39,14 +43,20 @@ PROMPT_AGAIN,

PROMPT_MORE,

PROMPT_QUIT,

PROMPT_ANSWERED,

+ PROMPT_NEXT,

};

const char *help_msg =

"The following commands are available:\n\n"

- "q: Quit\n"

- "N: Follow Nth link (where N is a number)\n"

- "b: Back (in the page history)\n"

- "f: Forward (in the page history)\n"

+ "q\tQuit\n"

+ "N\tFollow Nth link (where N is a number)\n"

+ "b\tBack (in the page history)\n"

+ "f\tForward (in the page history)\n"

+ "\n"

+ "Other commands include:\n\n"

+ "<Enter>\tread more lines\n"

+ "<url>\tgo to url\n"

+ "/<text>\tsearch for text (POSIX regular expression)\n"

;

static void

@@ -100,20 +110,17 @@ if (n == -1 && feof(browser->tty)) {

result = PROMPT_QUIT;

goto exit;

}

- if (strcmp(in, "\n") == 0) {

+ in[n - 1] = 0; // Remove LF

+

+ int r;

+ switch (in[0]) {

+ case '\0':

result = PROMPT_MORE;

goto exit;

- }

- if (strcmp(in, "q\n") == 0) {

+ case 'q':

result = PROMPT_QUIT;

goto exit;

- }

- if (strcmp(in, "?\n") == 0) {

- fprintf(browser->tty, "%s", help_msg);

- result = PROMPT_AGAIN;

- goto exit;

- }

- if (strcmp(in, "b\n") == 0) {

+ case 'b':

if (!browser->history->prev) {

fprintf(stderr, "At beginning of history\n");

result = PROMPT_AGAIN;

@@ -123,8 +130,7 @@ browser->history = browser->history->prev;

set_url(browser, browser->history->url, NULL);

result = PROMPT_ANSWERED;

goto exit;

- }

- if (strcmp(in, "f\n") == 0) {

+ case 'f':

if (!browser->history->next) {

fprintf(stderr, "At end of history\n");

result = PROMPT_AGAIN;

@@ -134,6 +140,25 @@ browser->history = browser->history->next;

set_url(browser, browser->history->url, NULL);

result = PROMPT_ANSWERED;

goto exit;

+ case '/':

+ if ((r = regcomp(&browser->regex, &in[1], REG_EXTENDED)) != 0) {

+ static char buf[1024];

+ r = regerror(r, &browser->regex, buf, sizeof(buf));

+ assert(r < (int)sizeof(buf));

+ fprintf(stderr, "Error: %s\n", buf);

+ result = PROMPT_AGAIN;

+ } else {

+ browser->searching = true;

+ result = PROMPT_ANSWERED;

+ }

+ goto exit_re;

+ case 'n':

+ result = PROMPT_NEXT;

+ goto exit_re;

+ case '?':

+ fprintf(browser->tty, "%s", help_msg);

+ result = PROMPT_AGAIN;

+ goto exit;

}

struct link *link = browser->links;

@@ -154,10 +179,14 @@ goto exit;

}

}

- in[n - 1] = 0; // Remove LF

set_url(browser, in, &browser->history);

result = PROMPT_ANSWERED;

exit:

+ if (browser->searching) {

+ browser->searching = false;

+ regfree(&browser->regex);

+ }

+exit_re:

free(in);

return result;

}

@@ -219,27 +248,34 @@

struct winsize ws;

ioctl(fileno(browser->tty), TIOCGWINSZ, &ws);

+ FILE *out = browser->tty;

+ bool searching = browser->searching;

+ if (searching) {

+ out = fopen("/dev/null", "w+");

+ }

+

char *text = NULL;

int row = 0, col = 0;

struct gemini_token tok;

struct link **next = &browser->links;

while (text != NULL || gemini_parser_next(&p, &tok) == 0) {

+repeat:

switch (tok.token) {

case GEMINI_TEXT:

- col += fprintf(browser->tty, " ");

+ col += fprintf(out, " ");

if (text == NULL) {

text = tok.text;

}

break;

case GEMINI_LINK:

if (text == NULL) {

- col += fprintf(browser->tty, "%d) ", nlinks++);

+ col += fprintf(out, "%d) ", nlinks++);

text = trim_ws(tok.link.text ? tok.link.text : tok.link.url);

*next = calloc(1, sizeof(struct link));

(*next)->url = strdup(trim_ws(tok.link.url));

next = &(*next)->next;

} else {

- col += fprintf(browser->tty, " ");

+ col += fprintf(out, " ");

}

break;

case GEMINI_PREFORMATTED:

@@ -247,44 +283,59 @@ continue; // TODO

case GEMINI_HEADING:

if (text == NULL) {

for (int n = tok.heading.level; n; --n) {

- col += fprintf(browser->tty, "#");

+ col += fprintf(out, "#");

}

switch (tok.heading.level) {

case 1:

- col += fprintf(browser->tty, " ");

+ col += fprintf(out, " ");

break;

case 2:

case 3:

- col += fprintf(browser->tty, " ");

+ col += fprintf(out, " ");

break;

}

text = trim_ws(tok.heading.title);

} else {

- col += fprintf(browser->tty, " ");

+ col += fprintf(out, " ");

}

break;

case GEMINI_LIST_ITEM:

if (text == NULL) {

- col += fprintf(browser->tty, " %s ",

+ col += fprintf(out, " %s ",

browser->unicode ? "•" : "*");

text = trim_ws(tok.list_item);

} else {

- col += fprintf(browser->tty, " ");

+ col += fprintf(out, " ");

}

break;

case GEMINI_QUOTE:

if (text == NULL) {

- col += fprintf(browser->tty, " %s ",

+ col += fprintf(out, " %s ",

browser->unicode ? "|" : "|");

text = trim_ws(tok.quote_text);

} else {

- col += fprintf(browser->tty, " ");

+ col += fprintf(out, " ");

}

break;

}

+ if (text && searching) {

+ int r = regexec(&browser->regex, text, 0, NULL, 0);

+ if (r != 0) {

+ text = NULL;

+ continue;

+ } else {

+ fclose(out);

+ row = col = 0;

+ out = browser->tty;

+ text = NULL;

+ searching = false;

+ goto repeat;

+ }

+ }

+

if (text) {

- int w = wrap(browser->tty, text, &ws, &row, &col);

+ int w = wrap(out, text, &ws, &row, &col);

text += w;

if (text[0] && row < ws.ws_row - 4) {

continue;

@@ -304,8 +355,9 @@

if (browser->pagination && row >= ws.ws_row - 4) {

char prompt[4096];

snprintf(prompt, sizeof(prompt), "\n%s at %s\n"

- "[Enter]: read more; [N]: follow Nth link; %s%s[q]uit; [?]; or type a URL\n"

+ "[Enter]: read more; %s[N]: follow Nth link; %s%s[q]uit; [?]; or type a URL\n"

"(more) => ", resp->meta, browser->plain_url,

+ browser->searching ? "[n]ext result; " : "",

browser->history->prev ? "[b]ack; " : "",

browser->history->next ? "[f]orward; " : "");

enum prompt_result result = PROMPT_AGAIN;

@@ -322,6 +374,10 @@ browser->running = false;

return true;

case PROMPT_ANSWERED:

return true;

+ case PROMPT_NEXT:

+ searching = true;

+ out = fopen("/dev/null", "w");

+ break;

}

row = col = 0;

@@ -533,6 +589,7 @@ case PROMPT_QUIT:

browser.running = false;

break;

case PROMPT_ANSWERED:

+ case PROMPT_NEXT:

break;

}