commit b009d1b81e1ed073de76b11d6b47ed8fcdc4ca0d
parent 8cae8e4f31cd38b441bc583c8038d212be7b5db8
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date: Sun, 20 Dec 2020 22:46:44 -0800
Add file downloading command `f`
Diffstat:
M | main.c | | | 122 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- |
1 file changed, 106 insertions(+), 16 deletions(-)
diff --git a/main.c b/main.c
@@ -22,6 +22,7 @@
#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <libgen.h>
#include <limits.h>
#include <netdb.h>
#include <poll.h>
@@ -32,10 +33,13 @@
#define LEN(X) (sizeof(X) / sizeof(*X))
+#define FETCH_MESSAGE
#define BUFSIZE 4096
#define ARGV_MAX 16
#define ARG_SEP ":\t "
+enum gphitem { GI_INFO, GI_PATH, GI_HOST, GI_PORT, GI_NULL };
+
static int timeout = 5 * 1000;
static char *gphfmt[] = { "gophmt", NULL };
@@ -255,7 +259,7 @@ gphsend(int sock, const char *request)
}
int
-gphcache(const char *cache, const char *host, const char *path, const char *port)
+selwrite(const char *host, const char *port, const char *selector, const char *file)
{
int sock;
@@ -263,8 +267,8 @@ gphcache(const char *cache, const char *host, const char *path, const char *port
if (sock == -1)
return 1;
- if (gphsend(sock, path) != 0 ||
- fetch(sock, cache) != 0) {
+ if (gphsend(sock, selector) != 0 ||
+ fetch(sock, file) != 0) {
close(sock);
return 1;
}
@@ -283,7 +287,7 @@ pipedup(int old, int new, int fildes[2])
}
int
-efmt(int (*putter)(const char *, int, const char **), const char *path, int argc, const char **argv)
+exfmt(int (*putter)(const char *, int, const char **), const char *path, int argc, const char **argv)
{
int fds[2];
int i;
@@ -354,25 +358,37 @@ cgoto(int argc, const char **argv, int depth, const char *tmpdir, const char *ho
}
int
-cback(int argc, const char **argv)
+strtorange(unsigned int min, int max, const char *s)
{
+ unsigned long n;
char *ep;
- unsigned long back;
+
+ n = strtoul(s, &ep, 0);
+ if (*s == '\0' || *ep != '\0') {
+ warnc(EINVAL, "not a number '%s'", s);
+ return -1;
+ } else if ((errno == ERANGE && n == ULONG_MAX) ||
+ n < min || n > max) {
+ warnc(ERANGE, "'%s'", s);
+ return -1;
+ }
+
+ return n;
+}
+
+int
+cback(int argc, const char **argv)
+{
+ int back;
wunused(1, argc, argv);
if (argc == 0)
return 1; /* back `1`, not an error */
- back = strtoul(argv[0], &ep, 0);
- if (*ep != '\0' || (back == 0 && errno == EINVAL)) {
- warnc(EINVAL, "'%s'", argv[0]);
+ back = strtorange(1, INT_MAX, argv[0]);
+ if (back < 0)
return 0;
- } else if (back > INT_MAX) {
- warnc(ERANGE, "'%s'", argv[0]);
- return 0;
- }
-
return back;
}
@@ -411,6 +427,77 @@ cmatch(const char *path, int argc, const char **argv)
return 0;
}
+/* argv[0] is the line index (starting from 0) of the file to
+ * download. If present argv[1] is the location to write the file
+ * to, otherwise it is written to the basename(3) of it's selector
+ * string.
+*/
+int
+cfetch(int argc, const char **argv, const char *cache, const char *host, const char *port)
+{
+ FILE *fp;
+ char *fields[GI_NULL + 1];
+ char *line;
+ const char *output;
+ int error;
+ int i, index;
+ size_t size;
+
+ if (argc == 0) {
+ warn("No index given");
+ return 1;
+ }
+ wunused(2, argc, argv);
+
+ index = strtorange(0, INT_MAX, argv[0]);
+ if (index < 0)
+ return 0;
+ if (argv[1])
+ output = argv[1];
+ else output = NULL;
+
+ fp = wfopen(cache, "r");
+ if (fp == NULL)
+ return 1;
+
+ error = 0;
+ line = NULL;
+ size = 0;
+ for (i = 0; getline(&line, &size, fp) != -1; ++i) {
+ if (i != index)
+ continue;
+ if (argsplit(fields, LEN(fields), line, "\t\r\n") != LEN(fields) - 1) {
+ warnx("Not a valid gopher item.");
+ ++error;
+ goto cleanup;
+ }
+ if (output == NULL)
+ if ((output = basename(fields[GI_PATH])) == NULL) {
+ warn("unable to get basename '%s'", fields[GI_PATH]);
+ ++error;
+ goto cleanup;
+ }
+ if (selwrite(fields[GI_HOST], fields[GI_PORT], fields[GI_PATH], output) != 0)
+ ++error;
+ goto cleanup;
+ }
+
+cleanup:
+#ifdef FETCH_MESSAGE
+ if (!error)
+ warnx("'%s/%s' written to '%s'", fields[GI_HOST], fields[GI_PATH], output);
+#endif
+ free(line);
+ if (wfclose(fp) || error)
+ return 1;
+ if (i < index) {
+ warnx("%d: Out of range.", i);
+ return 1;
+ }
+
+ return 0;
+}
+
int
execute(int command, int argc, const char **argv, int depth, const char *cache,
const char *tmpdir, const char *host, const char *path, const char *port)
@@ -420,7 +507,10 @@ execute(int command, int argc, const char **argv, int depth, const char *cache,
wunused(0, argc, argv);
return depth;
case 'p':
- efmt(cmatch, cache, argc, argv);
+ exfmt(cmatch, cache, argc, argv);
+ return 0;
+ case 'f':
+ cfetch(argc, argv, cache, host, port);
return 0;
case 'b':
return cback(argc, argv);
@@ -451,7 +541,7 @@ gawk(int depth, const char *tmpdir, const char *host, const char *path, const ch
unlinkit = 0;
else {
unlinkit = 1;
- if (gphcache(cache, host, path, port) == 1)
+ if (selwrite(host, port, path, cache) == 1)
return 0; /* let the user handle it */
}