gawk

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

commit 9787d16a53436f6c7bfeb54b03dd2b695981fa70
parent 54266ea5cdefc0a6f43401991033d6de035599ac
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date:   Sat, 19 Dec 2020 13:20:34 -0800

Add proper argument evaluation

This simplifies everything and allows easy reuse with other commands.
The `g` command actually works now too.

Diffstat:
Mmain.c | 133++++++++++++++++++++++++++++++++++++++++++-------------------------------------
1 file changed, 70 insertions(+), 63 deletions(-)

diff --git a/main.c b/main.c @@ -30,6 +30,8 @@ #define LEN(X) (sizeof(X) / sizeof(*X)) #define PROTO_MAX 16 #define BUFSIZE 4096 +#define ARGV_MAX 16 +#define ARG_SEP ":\t " static char *format_prog = "gophmt"; /* not released, costom formatter */ @@ -84,16 +86,14 @@ connect_to(const char *host, const char *proto) return s; } -char * +int input(char *buf, int size, const char *prompt, FILE *fp) { int len; - char *r; fputs(prompt, stdout); - r = fgets(buf, size, fp); - if (r == NULL) - return NULL; + if (fgets(buf, size, fp) == NULL) + return 1; len = strlen(buf); if (len >= size && buf[size - 1] != '\n') @@ -101,7 +101,7 @@ input(char *buf, int size, const char *prompt, FILE *fp) else if (len > 0) buf[len - 1] = '\0'; - return r; + return 0; } int @@ -134,33 +134,21 @@ newpath(char *path, char *newpath) } } -void -gawkparse(char *cmd, char *host, char *path, char *port) +int +argsplit(char **argv, int arg_max, char *s, const char *sep) { - int i; - char *parts[3]; - char *nhost, *npath, *nport; - - nhost = host; - npath = path; - nport = port; + int argc; - for (i = 0; i < 3; ++i) - parts[i] = strsep(&cmd, ":"); - - if (parts[1] == NULL) { - if (parts[0] != NULL) - npath = parts[0]; - } else { - if (parts[2] != NULL) - nport = parts[2]; - nhost = parts[0]; - npath = parts[1]; + for (argc = 0; argc < arg_max && + (argv[argc] = strsep(&s, sep)) != NULL;) { + if (*argv[argc] != '\0') + ++argc; } - strlcpy(host, nhost, HOST_NAME_MAX); - newpath(path, npath); - strlcpy(port, nport, PROTO_MAX); + if (argc == arg_max && + argv[argc - 1][strcspn(argv[argc - 1], sep)] == '\0') + return -1; + return argc; } /* NOTE: implement caching */ @@ -235,14 +223,12 @@ gawkat(int sock, const char *path) int gawk(int sock, int depth, const char *host, const char *path, const char *port) { - int done; - char cbuf[PATH_MAX + HOST_NAME_MAX + PROTO_MAX]; - char *cmd; - int closeit; - char npath[PATH_MAX]; - char nhost[HOST_NAME_MAX]; - char nport[PROTO_MAX]; + char *argv[ARGV_MAX]; + char inbuf[PATH_MAX + HOST_NAME_MAX + PROTO_MAX]; char prompt[64]; /* you wouldn't want it longer anyway */ + const char *nreq[3]; + int argc; + int closeit, done, newsock; ++depth; @@ -258,52 +244,73 @@ gawk(int sock, int depth, const char *host, const char *path, const char *port) snprintf(prompt, sizeof(prompt), "[%s] %s ", host, path); done = 0; - while (!done && (cmd = input(cbuf, sizeof(cbuf), prompt, stdin))) { - /* `strcpy(cmd, "p")` works I suppose, but I like - * goto and it's more efficient. */ - if (*cmd == '\0') - goto gawk_at; + while (!done && input(inbuf, sizeof(inbuf), prompt, stdin) == 0) { + argc = argsplit(argv, LEN(argv), inbuf, ARG_SEP); + if (argc <= 0) { + if (argc < 0) + warn("Too many arguments"); + continue; + } - switch (*cmd++) { + if (strlen(*argv) > 1) + goto invalid_input; + switch (**argv) { case 'q': /* quit */ + if (argc > 1) + goto invalid_input; done = depth; break; case 'p': /* print */ -gawk_at: puts("gawking"); /* NOTE: SIGPIPE on second call. * gawkat(sock, path); */ break; case 'b': /* back */ - if (*cmd == '\0') + if (argc > 2) + goto invalid_input; + if (argc == 1) done = 1; - else { - done = strtoui(cmd); - if (done < 0) { - warnx("invalid argument for 'b', '%s'", cmd); - done = 0; - } - } + else if (argc == 2) + done = strtoui(argv[1]); break; case 'g': /* goto or gawk */ - if (*cmd == '\0') { - warnx("'g': No arguments."); - break; + if (argc > 4) + goto invalid_input; + + newsock = 0; + nreq[0] = host; + nreq[1] = path; + nreq[2] = port; + + /* NOTE: relative path's not implemented */ + if (argc == 1) + nreq[1] = "/"; + else if (argc == 2) + nreq[1]= argv[1]; + else { + nreq[0] = argv[1]; + nreq[1] = argv[2]; + if (strcmp(host, nreq[0]) != 0) + newsock = -1; + if (argc == 3) { + nreq[2] = argv[3]; + if (!newsock && strcmp(port, nreq[2]) != 0) + newsock = -1; + } } - gawkparse(cmd, nhost, npath, nport); - if (strcmp(host, nhost) != 0 || strcmp(port, nport) != 0) - done = gawk(-1, depth, nhost, npath, nport); - else - done = gawk(sock, depth, nhost, npath, nport); + + if (!newsock) + newsock = sock; + done = gawk(newsock, depth, nreq[0], nreq[1], nreq[2]); break; default: - warnx("invalid command '%c'", *--cmd); +invalid_input: + warnc(EINVAL, "invalid command or arguments for '%s'", argv[0]); } } - if (closeit) - if (close(sock) == -1) - warn("unable to disconnect"); + if (closeit && close(sock) == -1) + warn("unable to disconnect from '%s'", host); if (ferror(stdin)) { warn("'stdin'"); return -1;