cgmnlm

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

commit fa78663748958eebb442f03d0406f42f6190522a
parent 9ddd5c16dae4b556c7aeac88c219568c479d87f2
Author: Eyal Sawady <ecs@d2evs.net>
Date:   Tue, 20 Oct 2020 12:55:39 -0400

Add 'd' to download page

Diffstat:
Mdoc/gmni.scd | 4++++
Minclude/util.h | 2++
Msrc/gmni.c | 13++++++++++++-
Msrc/gmnlm.c | 23++++++++++++++++++++++-
Msrc/tofu.c | 1+
Msrc/util.c | 46++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/doc/gmni.scd b/doc/gmni.scd @@ -68,3 +68,7 @@ performed with the user's input supplied to the server. Suppress the input prompt if the server requests an input, and instead print a diagnostic message and exit with a zero (successful) status code. + +*-o* _path_ + Write output to _path_. If _path_ ends with a '/', the basename of the URL + will be appended. If _path_ is the empty string, "./" will be presumed. diff --git a/include/util.h b/include/util.h @@ -8,5 +8,7 @@ struct pathspec { char *getpath(const struct pathspec *paths, size_t npaths); int mkdirs(char *path, mode_t mode); +int download_resp(FILE *out, struct gemini_response resp, const char *path, + char *url); #endif diff --git a/src/gmni.c b/src/gmni.c @@ -14,6 +14,7 @@ #include <unistd.h> #include "gmni.h" #include "tofu.h" +#include "util.h" static void usage(const char *argv_0) @@ -125,6 +126,8 @@ main(int argc, char *argv[]) enum input_mode input_mode = INPUT_READ; FILE *input_source = stdin; + char *output_file = NULL; + bool follow_redirects = false, linefeed = true; int max_redirect = 5; @@ -136,7 +139,7 @@ main(int argc, char *argv[]) cfg.action = TOFU_ASK; int c; - while ((c = getopt(argc, argv, "46d:D:E:hj:lLiINR:")) != -1) { + while ((c = getopt(argc, argv, "46d:D:E:hj:lLiINR:o:")) != -1) { switch (c) { case '4': hints.ai_family = AF_INET; @@ -204,6 +207,9 @@ main(int argc, char *argv[]) return 1; } break; + case 'o': + output_file = optarg; + break; default: fprintf(stderr, "fatal: unknown flag %c\n", c); return 1; @@ -304,6 +310,11 @@ main(int argc, char *argv[]) break; } + if (output_file != NULL) { + ret = download_resp(stderr, resp, output_file, url); + break; + } + char last; char buf[BUFSIZ]; for (int n = 1; n > 0;) { diff --git a/src/gmnlm.c b/src/gmnlm.c @@ -78,6 +78,7 @@ const char *help_msg = "m\tSave bookmark\n" "M\tBrowse bookmarks\n" "r\tReload the page\n" + "d <path>\tDownload page to <path>\n" "\n" "Other commands include:\n\n" "<Enter>\tread more lines\n" @@ -553,6 +554,24 @@ do_prompts(const char *prompt, struct browser *browser) ? strchr(browser->meta, ';') : NULL); result = PROMPT_AGAIN; goto exit; + case 'd': + if (in[1] != '\0' && !isspace(in[1])) break; + struct gemini_response resp; + char url[1024] = {0}; + strncpy(&url[0], browser->plain_url, sizeof(url)); + // XXX: may affect history, do we care? + enum gemini_result res = do_requests(browser, &resp); + if (res != GEMINI_OK) { + fprintf(stderr, "Error: %s\n", + gemini_strerr(res, &resp)); + result = PROMPT_AGAIN; + goto exit; + } + set_url(browser, url, NULL); + download_resp(browser->tty, resp, trim_ws(&in[1]), url); + gemini_response_finish(&resp); + result = PROMPT_AGAIN; + goto exit; case '?': if (in[1]) break; fprintf(browser->tty, "%s", help_msg); @@ -849,7 +868,9 @@ display_response(struct browser *browser, struct gemini_response *resp) if (strncmp(resp->meta, "text/", 5) == 0) { return display_plaintext(browser, resp); } - assert(0); // TODO: Deal with other mimetypes + fprintf(stderr, "Media type %s is unsupported, use \"d <path>\" to download this page\n", + resp->meta); + return false; } static enum tofu_action diff --git a/src/tofu.c b/src/tofu.c @@ -10,6 +10,7 @@ #include <stdio.h> #include <string.h> #include <time.h> +#include "gmni.h" #include "tofu.h" #include "util.h" diff --git a/src/util.c b/src/util.c @@ -2,9 +2,12 @@ #include <errno.h> #include <libgen.h> #include <limits.h> +#include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> +#include "gmni.h" #include "util.h" static void @@ -60,3 +63,46 @@ getpath(const struct pathspec *paths, size_t npaths) { } return NULL; } + +int +download_resp(FILE *out, struct gemini_response resp, const char *path, + char *url) +{ + char buf[PATH_MAX]; + assert(path); + if (path[0] == '\0') { + path = "./"; + } + if (path[strlen(path)-1] == '/') { + strncat(strncpy(&buf[0], path, sizeof(buf)), basename(url), + sizeof(buf)); + path = &buf[0]; + } + FILE *f = fopen(path, "w"); + if (f == NULL) { + fprintf(stderr, "Could not open %s for writing: %s\n", + path, strerror(errno)); + return 1; + } + fprintf(out, "Downloading %s to %s\n", url, path); + for (int n = 1; n > 0;) { + n = BIO_read(resp.bio, buf, BUFSIZ); + if (n == -1) { + fprintf(stderr, "Error: read\n"); + return 1; + } + ssize_t w = 0; + while (w < (ssize_t)n) { + ssize_t x = fwrite(&buf[w], 1, n - w, f); + if (ferror(f)) { + fprintf(stderr, "Error: write: %s\n", + strerror(errno)); + return 1; + } + w += x; + } + } + fprintf(out, "Finished download\n"); + fclose(f); + return 0; +}