gawk

[old] Sed-like interface to the Gopher protocol
Log | Files | Refs | LICENSE

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:
Mcommand.c | 8+++++---
Mgawk.h | 5+++--
Mmain.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; }