cgmnlm

colorful gemini line mode browser
git clone https://git.clttr.info/cgmnlm.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

commit 563922a7e2da77b3973dcf707854121932ca244e
parent b90888e71878eea7727cd507503198d134d91ab7
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 20 Sep 2020 20:01:31 -0400

Merge normal mode & pagination prompts

Diffstat:
Msrc/gmnlm.c | 219+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 125 insertions(+), 94 deletions(-)

diff --git a/src/gmnlm.c b/src/gmnlm.c @@ -27,9 +27,18 @@ struct browser { 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 @@ set_url(struct browser *browser, char *new_url, struct history **history) 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 @@ trim_ws(char *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 @@ display_gemini(struct browser *browser, struct gemini_response *resp) ++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] "); - - size_t n = 0; - char *l = NULL; - if (getline(&l, &n, browser->tty) == -1) { - return; + 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); } - 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 @@ display_plaintext(struct browser *browser, struct gemini_response *resp) } (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 @@ get_input(const struct gemini_response *resp, FILE *source) 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 @@ do_requests(struct browser *browser, struct gemini_response *resp) 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 @@ do_requests(struct browser *browser, struct gemini_response *resp) break; case GEMINI_STATUS_CLASS_SUCCESS: requesting = false; - display_response(browser, resp); - break; + return display_response(browser, resp); } if (requesting) { @@ -287,63 +360,7 @@ do_requests(struct browser *browser, struct gemini_response *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 @@ main(int argc, char *argv[]) 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;