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:
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;
+}