commit 0fb8df381a5af6c001840c17aec91c3e20b730d6
parent dbca66f21fa2dd694b9b138d4385437e61d7d034
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date: Tue, 5 Jan 2021 19:10:54 -0800
Support plain-text items
Remove terminator from the end and remove one leading period from
a line with multiple.
Also rename MY_CHUNK_SIZE to MY_CHUNK_MAX.
Diffstat:
M | command.c | | | 8 | +++++--- |
M | gawk.h | | | 5 | +++-- |
M | main.c | | | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
3 files changed, 62 insertions(+), 19 deletions(-)
diff --git a/command.c b/command.c
@@ -54,10 +54,10 @@ cextern(int argc, char const **argv, int ino, char const **item)
}
LOCAL int
-timid_gopher(char const **addr, char const *path)
+timid_gopher(char const **addr, char const *path, int bin)
{
if (!exists(path))
- return gopher(addr, path);
+ return gopher(addr, path, bin);
warn(EEXIST, "'%s'", path);
return 1;
}
@@ -66,6 +66,7 @@ int
cfetch(int argc, char const **argv, int ino, char const **item)
{
char const *path;
+ int bin;
if (warg(1, 2, argc, argv))
return FAIL;
@@ -80,7 +81,8 @@ cfetch(int argc, char const **argv, int ino, char const **item)
}
}
- if (timid_gopher(itemtoaddr(item), path))
+ bin = **item == '0' ? 0 : 1;
+ if (timid_gopher(itemtoaddr(item), path, bin))
return NEXT;
warn(0, "%s: '%s%s' > '%s'", *argv, item[GI_HOST], item[GI_PATH], path);
return PASS;
diff --git a/gawk.h b/gawk.h
@@ -2,10 +2,11 @@
#define _gawk_h
#define LEN(X) (sizeof(X) / sizeof(*X))
+#define MAX(A,B) (A > B ? A : B)
/* arbitrary limits */
#define MY_ARGV_MAX 12
-#define MY_CHUNK_SIZE 512 /* for slurping data, not recursive */
+#define MY_CHUNK_MAX 512 /* for slurping data, not recursive */
#define MY_FILTER_MAX 4
#define MY_INPUT_MAX 128
#define MY_LINE_MAX 256
@@ -28,7 +29,7 @@ struct command {
};
char const **itemtoaddr(char const **);
-int gopher(char const **, char const *);
+int gopher(char const **, char const *, int);
int gawk(char const **);
#endif /* _gawk_h */
diff --git a/main.c b/main.c
@@ -164,12 +164,54 @@ gph_send(int sock, char const *const message)
return 0;
}
+ssize_t
+recvtxt(int s, FILE *output)
+{
+ FILE *input;
+ char *bufp;
+ /* NOTE: this buffer should be bigger than your average line */
+ char buf[MAX(MY_LINE_MAX, MY_CHUNK_MAX)];
+
+ input = fdopen(s, "r");
+ if (input == NULL) {
+ warn(errno, "fdopen");
+ return 1;
+ }
+
+ /*
+ * While the specification says strip leading periods if
+ * there are multiple I think it must mean only one (see
+ * RFC 1436 Appendix).
+ */
+ while ((bufp = fgets(buf, sizeof(buf), input)) != NULL) {
+ if (*bufp == '.')
+ if (strcmp(++bufp, "\r\n") == 0)
+ break;
+ if (fputs(bufp, output) == EOF)
+ return 1;
+ }
+
+ return 0;
+}
+
int
-gph_recvto(int sock, char const *const path)
+recvbin(int s, FILE *output)
{
- FILE *fp;
- char buf[MY_CHUNK_SIZE];
+ char buf[MY_CHUNK_MAX];
ssize_t bytes;
+
+ while ((bytes = recv(s, buf, sizeof(buf), 0)) > 0)
+ /* NOTE: cast is safe, bytes _is_ >0 */
+ if (fwrite(buf, 1, bytes, output) != (size_t)bytes)
+ return 1;
+ return 0;
+}
+
+int
+gph_recvto(int sock, char const *const path, int bin)
+{
+ FILE *fp;
+ int status;
struct pollfd pfd;
pfd.fd = sock;
@@ -183,18 +225,16 @@ gph_recvto(int sock, char const *const path)
if (fp == NULL)
return 1;
- while ((bytes = recv(sock, buf, sizeof(buf), 0)) > 0) {
- if (fwrite(buf, 1, bytes, fp) != (size_t)bytes) {
- wfclose(fp);
- return 1;
- }
- }
-
- return wfclose(fp);
+ if (bin)
+ status = recvbin(sock, fp);
+ else status = recvtxt(sock, fp);
+ if (wfclose(output) || status)
+ return 1;
+ return 0;
}
int
-gopher(char const **addr, char const *const path)
+gopher(char const **addr, char const *const path, int bin)
{
int sock;
@@ -203,7 +243,7 @@ gopher(char const **addr, char const *const path)
return 1;
if (gph_send(sock, addr[AR_PATH]) != 0 ||
- gph_recvto(sock, path) != 0) {
+ gph_recvto(sock, path, bin) != 0) {
close(sock);
return 1;
}
@@ -351,7 +391,7 @@ gawk(char const **addr)
mycache = 0;
else {
mycache = 1;
- if (gopher(addr, cache) == 1)
+ if (gopher(addr, cache, 0) == 1)
return ERROR;
}