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

index 2c2b67e0e56d2e6be04bba6cee96cb4a24ff3b51..3598e3fb1770aca2bd646d7a3f2bf642b54c2ab9 100644

--- a/src/gmnlm.c

+++ b/src/gmnlm.c

@@ -27,9 +27,18 @@ bool pagination;

struct gemini_options opts;

FILE *tty;

+ char *plain_url;

struct Curl_URL *url;

struct link *links;

struct history *history;

+ bool running;

+};

+

+enum prompt_result {

+ PROMPT_AGAIN,

+ PROMPT_MORE,

+ PROMPT_QUIT,

+ PROMPT_ANSWERED,

};

static void

@@ -70,6 +79,63 @@ }

return true;

}

+static enum prompt_result

+do_prompts(const char *prompt, struct browser *browser)

+{

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

+

+ size_t l = 0;

+ char *in = NULL;

+ ssize_t n = getline(&in, &l, browser->tty);

+ if (n == -1 && feof(browser->tty)) {

+ return PROMPT_QUIT;

+ }

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

+ return PROMPT_MORE;

+ }

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

+ return PROMPT_QUIT;

+ }

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

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

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

+ return PROMPT_AGAIN;

+ }

+ browser->history = browser->history->prev;

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

+ return PROMPT_ANSWERED;

+ }

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

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

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

+ return PROMPT_AGAIN;

+ }

+ browser->history = browser->history->next;

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

+ return PROMPT_ANSWERED;

+ }

+

+ struct link *link = browser->links;

+ char *endptr;

+ int linksel = (int)strtol(in, &endptr, 10);

+ if (endptr[0] == '\n' && linksel >= 0) {

+ while (linksel > 0 && link) {

+ link = link->next;

+ --linksel;

+ }

+

+ if (!link) {

+ fprintf(stderr, "Error: no such link.\n");

+ } else {

+ set_url(browser, link->url, &browser->history);

+ return PROMPT_ANSWERED;

+ }

+ }

+ free(in);

+

+ return PROMPT_AGAIN;

+}

+

static char *

trim_ws(char *in)

{

@@ -80,7 +146,7 @@ for (; *in && isspace(*in); ++in);

return in;

}

-static void

+static bool

display_gemini(struct browser *browser, struct gemini_response *resp)

{

// TODO: Strip ANSI escape sequences

@@ -136,29 +202,36 @@ }

++row;

col = 0;

- // TODO: It would be nice if we could follow links from this

- // prompt

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

- fprintf(browser->tty, "[Enter for more, or q to stop] ");

+ 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; [b]ack; [f]orward; [q]uit\n"

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

+ enum prompt_result result = PROMPT_AGAIN;

+ while (result == PROMPT_AGAIN) {

+ result = do_prompts(prompt, browser);

+ }

- size_t n = 0;

- char *l = NULL;

- if (getline(&l, &n, browser->tty) == -1) {

- return;

- }

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

- return;

+ switch (result) {

+ case PROMPT_AGAIN:

+ case PROMPT_MORE:

+ break;

+ case PROMPT_QUIT:

+ browser->running = false;

+ return true;

+ case PROMPT_ANSWERED:

+ return true;

}

- free(l);

row = col = 0;

}

}

gemini_parser_finish(&p);

+ return false;

}

-static void

+static bool

display_plaintext(struct browser *browser, struct gemini_response *resp)

{

// TODO: Strip ANSI escape sequences

@@ -175,20 +248,20 @@ }

}

(void)row; (void)col; // TODO: generalize pagination

+ return false;

}

-static void

+static bool

display_response(struct browser *browser, struct gemini_response *resp)

{

if (strcmp(resp->meta, "text/gemini") == 0

|| strncmp(resp->meta, "text/gemini;", 12) == 0) {

- display_gemini(browser, resp);

- return;

+ return display_gemini(browser, resp);

}

if (strncmp(resp->meta, "text/", 5) == 0) {

- display_plaintext(browser, resp);

- return;

+ return display_plaintext(browser, resp);

}

+ assert(0); // TODO: Deal with other mimetypes

}

static char *

@@ -225,19 +298,19 @@ }

return input;

}

-static char *

+// Returns true to skip prompting

+static bool

do_requests(struct browser *browser, struct gemini_response *resp)

{

- char *plain_url;

int nredir = 0;

bool requesting = true;

while (requesting) {

CURLUcode uc = curl_url_get(browser->url,

- CURLUPART_URL, &plain_url, 0);

+ CURLUPART_URL, &browser->plain_url, 0);

assert(uc == CURLUE_OK); // Invariant

- enum gemini_result res = gemini_request(

- plain_url, &browser->opts, resp);

+ enum gemini_result res = gemini_request(browser->plain_url,

+ &browser->opts, resp);

if (res != GEMINI_OK) {

fprintf(stderr, "Error: %s\n", gemini_strerr(res, resp));

requesting = false;

@@ -253,7 +326,8 @@ requesting = false;

break;

}

- char *new_url = gemini_input_url(plain_url, input);

+ char *new_url = gemini_input_url(

+ browser->plain_url, input);

assert(new_url);

set_url(browser, new_url, NULL);

break;

@@ -278,8 +352,7 @@ resp->status, resp->meta);

break;

case GEMINI_STATUS_CLASS_SUCCESS:

requesting = false;

- display_response(browser, resp);

- break;

+ return display_response(browser, resp);

}

if (requesting) {

@@ -287,63 +360,7 @@ gemini_response_finish(resp);

}

}

- return plain_url;

-}

-

-static bool

-do_prompts(const char *prompt, struct browser *browser)

-{

- bool prompting = true;

- while (prompting) {

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

-

- size_t l = 0;

- char *in = NULL;

- ssize_t n = getline(&in, &l, browser->tty);

- if (n == -1 && feof(browser->tty)) {

- return false;

- }

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

- return false;

- }

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

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

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

- continue;

- }

- browser->history = browser->history->prev;

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

- break;

- }

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

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

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

- continue;

- }

- browser->history = browser->history->next;

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

- break;

- }

-

- struct link *link = browser->links;

- char *endptr;

- int linksel = (int)strtol(in, &endptr, 10);

- if (endptr[0] == '\n' && linksel >= 0) {

- while (linksel > 0 && link) {

- link = link->next;

- --linksel;

- }

-

- if (!link) {

- fprintf(stderr, "Error: no such link.\n");

- } else {

- set_url(browser, link->url, &browser->history);

- break;

- }

- }

- free(in);

- }

- return true;

+ return false;

}

int

@@ -381,24 +398,38 @@ SSL_load_error_strings();

ERR_load_crypto_strings();

browser.opts.ssl_ctx = SSL_CTX_new(TLS_method());

- bool run = true;

struct gemini_response resp;

- while (run) {

+ browser.running = true;

+ while (browser.running) {

static char prompt[4096];

- char *plain_url = do_requests(&browser, &resp);

+ if (do_requests(&browser, &resp)) {

+ // Skip prompts

+ goto next;

+ }

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

- "[n]: follow Nth link; [o <url>]: open URL; "

- "[b]ack; [f]orward; "

- "[q]uit\n"

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

+ "[n]: follow Nth link; [b]ack; [f]orward; [q]uit\n"

"=> ",

- resp.status == GEMINI_STATUS_SUCCESS ? " " : "",

resp.status == GEMINI_STATUS_SUCCESS ? resp.meta : "",

- plain_url);

+ browser.plain_url);

gemini_response_finish(&resp);

- run = do_prompts(prompt, &browser);

+ enum prompt_result result = PROMPT_AGAIN;

+ while (result == PROMPT_AGAIN || result == PROMPT_MORE) {

+ result = do_prompts(prompt, &browser);

+ }

+ switch (result) {

+ case PROMPT_AGAIN:

+ case PROMPT_MORE:

+ assert(0);

+ case PROMPT_QUIT:

+ browser.running = false;

+ break;

+ case PROMPT_ANSWERED:

+ break;

+ }

+next:;

struct link *link = browser.links;

while (link) {

struct link *next = link->next;