gawk

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

commit 30229cba3c91d512f1190c770268f2d7e07cbd57
parent 2aa849677382af4d00a99adaefa84eeecdcbb21d
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date:   Tue, 22 Dec 2020 18:43:25 -0800

Allow chaining filteres

Multiple filters are now allowed and only one funtion does the I/O
cutting down on duplicate code.

Diffstat:
Mmain.c | 213+++++++++++++++++++++++++++++++++++++------------------------------------------
1 file changed, 101 insertions(+), 112 deletions(-)

diff --git a/main.c b/main.c @@ -41,13 +41,14 @@ #define MY_LINE_MAX 128 #define MY_PATH_MAX 128 /* NOTE: PATH_MAX should be used with realpath(1), etc. */ #define MY_URL_MAX 72 +#define MY_FILTER_MAX 4 enum gphitem { GI_INFO, GI_PATH, GI_HOST, GI_PORT, GI_NULL }; /* NOTE: before changing see reqtoaddr() */ enum address { AR_PATH, AR_HOST, AR_PORT, AR_NULL }; typedef int (command)(int, const char **, int, const char **); -typedef int (filter)(const char *, int, const char **, command); +typedef int (filter)(int, const char **, int, const char **, command); typedef int (stack_command)(int, const char **, int, const char **); int gawk(const char **); @@ -87,11 +88,17 @@ wfclose(FILE *fp) return 0; } -void -wunused(int max, int argc, const char **ap) +int +badargs(int min, int max, int argc, const char **ap) { - if (argc > max) + if (max > 0 && argc > max) { warnx("warning: '%s' and %d more arguments unused.", ap[max], argc - (max + 1)); + } else if (argc < min) { + warnx("error: Not enough arguments."); + return 1; + } + + return 0; } int @@ -318,108 +325,83 @@ gph_write(const char **addr, const char *path) return close(sock); } +/* Returns -1 on fatal error, 0 on no match, and 1 on match. */ int -splitrun(int argc, const char **argv, int index, char *request, command *func) +splitrun(filter **filters, int argc, const char **argv, int index, char *s, command *func) { - char *fields[6]; + int i, n; + char *item[6]; /* NOTE: argsplit ignores multiple terminators. */ - if (argsplit(fields, LEN(fields), request, "\t\r\n") != 4) - if (fields[4] != NULL && strcmp(fields[4], "+") != 0) { + if (argsplit(item, LEN(item), s, "\t\r\n") != 4) + /* NOTE: not foolproof */ + if (item[4] != NULL && strcmp(item[4], "+") != 0) { warnx("%d: Not a valid gopher item.", index); return 1; } - return func(argc, argv, index, (const char **)fields); -} -int -findex(const char *cache, int argc, const char **argv, command *func) -{ - FILE *fp; - char buf[MY_LINE_MAX]; - int more; - unsigned int i, j; - unsigned int indexes[argc]; - int ids; - - fp = fopen(cache, "r"); - if (fp == NULL) - return 1; - - /* NOTE: One could optomize by sorting these indexes. */ - for (ids = 0; ids < argc && isdigit(*argv[ids]); ++ids) { - if (strtorange(&indexes[ids], 0, UINT_MAX, argv[ids]) != 0) - return 1; - - } - argc -= ids; - argv += ids; - - more = 1; - for (i = 0; more && fgets(buf, sizeof(buf), fp) != NULL; ++i) { - if (*buf == '.') - break; - more = 0; - for (j = 0; j < ids; ++j) - if (i == indexes[j]) - splitrun(argc, argv, i, buf, func); /* NOTE: errors ignored */ - else if (i < indexes[j]) - more = 1; + for (i = 0; filters[i] != NULL; ++i) { + n = filters[i](argc, argv, index, (const char **)item, func); + if (n <= 0) + return n; + argc -= n; + argv += n; } - return wfclose(fp); + func(argc, argv, index, (const char **)item); + return 0; } int -fstrmatch(const char *cache, int argc, const char **argv, command *func) +run_filters(const char *cache, filter **filters, int argc, const char **argv, command *func) { FILE *fp; - char buf[MY_LINE_MAX]; - int anystr; + char item[MY_LINE_MAX]; int i; fp = wfopen(cache, "r"); if (fp == NULL) return 1; - if (!argv[0]) - anystr = 1; - else anystr = 0; - - for (i = 0; fgets(buf, sizeof(buf), fp) != NULL; ++i) { - if (*buf == '.') - break; - if (anystr || strcasestr(buf, argv[0])) - splitrun(argc - 1, argv + 1, i, buf, func); + for (i = 0; fgets(item, sizeof(item), fp) != NULL && *item != '.'; ++i) { + if (splitrun(filters, argc, argv, i, item, func) < 0) + return -1; } - return wfclose(fp); } int -fgroup(const char *cache, int argc, const char **argv, command *func) +findex(int argc, const char **argv, int index, const char **item, command *func) { - FILE *fp; - char buf[MY_LINE_MAX]; - int anytype; - int i; + unsigned int n; - fp = wfopen(cache, "r"); - if (fp == NULL) + if (badargs(1, -1, argc, argv)) + return -1; + if (strtorange(&n, 0, UINT_MAX, argv[0])) + return -1; + if (index == n) return 1; + return 0; +} - if (!argv[0] || strchr(argv[0], '.')) - anytype = 1; - else anytype = 0; - - for (i = 0; fgets(buf, sizeof(buf), fp) != NULL; ++i) { - if (*buf == '.') - break; - if (anytype || strchr(argv[0], *buf)) - splitrun(argc - 1, argv + 1, i, buf, func); - } +int +fstring(int argc, const char **argv, int index, const char **item, command *func) +{ + if (badargs(1, -1, argc, argv)) + return -1; + if (strcasestr(item[GI_INFO], argv[0]) != NULL) + return 1; + return 0; +} - return wfclose(fp); +int +ftype(int argc, const char **argv, int index, const char **item, command *func) +{ + if (badargs(1, -1, argc, argv)) + return -1; + if (strchr(argv[0], *item[GI_INFO]) != NULL) + return 1; + return 0; } /* If lacking arguments goes to root (`/'). If one argument is given @@ -432,7 +414,8 @@ sgoto(int argc, const char **argv, int depth, const char **addr) char tmp[MY_PATH_MAX]; const char *newaddr[AR_NULL]; - wunused(3, argc, argv); + if (badargs(0, 3, argc, argv)) + return 1; strcpy(tmp, addr[AR_PATH]); newaddr[AR_HOST] = addr[AR_HOST]; @@ -458,9 +441,7 @@ sunwind(int argc, const char **argv, int depth, const char **addr) { unsigned int n; - wunused(1, argc, argv); - - if (argc == 0) + if (badargs(1, 1, argc, argv)) return 1; if (strtorange(&n, 1, INT_MAX, argv[0])) return 0; @@ -470,14 +451,14 @@ sunwind(int argc, const char **argv, int depth, const char **addr) int sexit(int argc, const char **argv, int depth, const char **addr) { - wunused(0, argc, argv); + badargs(0, 0, argc, argv); return depth; } int cprint(int argc, const char **argv, int i, const char **s) { - wunused(0, argc, argv); + badargs(0, 0, argc, argv); if (**s == 'i') printf(" %s\n", s[GI_INFO] + 1); @@ -498,7 +479,8 @@ cfetch(int argc, const char **argv, int i, const char **request) { const char *output; - wunused(1, argc, argv); + if (badargs(0, 1, argc, argv)) + return 1; if (argc > 0) output = argv[0]; @@ -564,12 +546,12 @@ filter * getfilter(int c) { switch (c) { - case 'g': - return fgroup; + case 't': + return ftype; case 'i': return findex; case 's': - return fstrmatch; + return fstring; default: return NULL; } @@ -594,38 +576,45 @@ int execute(int argc, const char **argv, int depth, const char *cache, const char **addr) { - command *c; - filter *f; - stack_command *sc; + int i; + filter *filters[MY_FILTER_MAX]; + command *command; + stack_command *stackc; - if (argc == 0) + if (argc == 0 && argv[0][0] == '\0') return 0; - sc = NULL; - switch (strlen(argv[0])) { - case 0: - return 0; /* ignore empty */ - case 1: - f = fgroup; - c = getcommand(argv[0][0]); - if (c == NULL) - sc = getstackc(argv[0][0]); - break; - case 2: - f = getfilter(argv[0][0]); - c = getcommand(argv[0][1]); - break; - default: -invalid_statement: - warnx("invalid statement '%s'", *argv); - return 0; + filters[0] = NULL; + stackc = 0; + + for (i = 0; argv[0][i + 1] != '\0'; ++i) { + if (i >= LEN(filters)) { + warnx("Too many filters"); + return 0; + } + filters[i] = getfilter(argv[0][i]); + if (filters[i] == NULL) { + warnx("'%c': Not a filter.", argv[0][i]); + return 0; + } + } + filters[i] = NULL; + command = getcommand(argv[0][i]); + if (command == NULL) { + stackc = getstackc(argv[0][i]); + if (stackc == NULL) { + warnx("'%c': Not a command.", argv[0][i]); + return 0; + } } - if (sc != NULL) - return sc(argc - 1, argv + 1, depth, addr); - if (f == NULL || c == NULL) - goto invalid_statement; - f(cache, argc - 1, argv + 1, c); + /* TODO: combine stack commands and regular commands */ + --argc; + ++argv; + if (stackc != NULL) + return stackc(argc, argv, depth, addr); + else + run_filters(cache, filters, argc, argv, command); return 0; }