ref: cee3145ab92a46565ddbf00c2cc034464bc8002f
author: Tevo <estevan.cps@gmail.com>
date: Thu Nov 5 21:25:29 EST 2020
Somewhat broken prototype
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,21 @@
+</$objtype/mkfile
+
+OBJ=\
+ waffle.$O
+
+%.$O: %.c
+ $CC $CFLAGS $prereq
+
+$O.out: $OBJ
+ $LD $prereq
+
+all:V: $O.out
+
+install:V: all
+ cp $O.out /$objtype/bin/waffle
+
+uninstall:V:
+ rm -f /$objtype/bin/waffle
+
+clean nuke:V:
+ rm -f *.[$OS] $O.out
--- /dev/null
+++ b/waffle.c
@@ -1,0 +1,365 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <bio.h>
+#include <String.h>
+
+char *stubhost = "error.host\t1";
+char *spacetab = " ";
+char *srvroot = "./";
+char *defprog = " \
+ # This is a comment and shouldn't be considered\n\
+ info system is $sysname on $cputype\n\
+ info $objtype \\$notavar $nonexistant \\\\$yay\n\
+ lsdir .\n \
+";
+
+enum
+{+ OP_COMMENT,
+ OP_LSDIR,
+ OP_EXEC,
+};
+
+enum
+{+ /* Unofficial */
+ GOPHER_INFO = 'i',
+ GOPHER_DOC = 'd',
+ GOPHER_HTML = 'h',
+ GOPHER_AUDIO = 's',
+ /* RFC1436 */
+ GOPHER_FILE = '0',
+ GOPHER_DIR,
+ GOPHER_PHONEBOOK,
+ GOPHER_ERR,
+ GOPHER_MAC_FILE,
+ GOPHER_DOS_FILE,
+ GOPHER_UNIX_FILE,
+ GOPHER_SEARCH,
+ GOPHER_TELNET,
+ GOPHER_BIN,
+ GOPHER_REDUNDANT = '+',
+ GOPHER_TN3270 = 'T',
+ GOPHER_GIF = 'g',
+ GOPHER_IMAGE = 'I',
+};
+
+void
+usage(void)
+{+ fprint(2, "usage: [-r root] [-d defprog] %s", argv0);
+ exits("usage");+}
+
+int
+opforcmd(char* cmd)
+{+ struct
+ {+ char *k;
+ int op;
+ } ops[] = {+ {"#", OP_COMMENT},+ {"lsdir", OP_LSDIR},+ {"exec", OP_EXEC},+
+ {"info", GOPHER_INFO},+ {"doc", GOPHER_DOC},+ {"html", GOPHER_HTML},+
+ {"file", GOPHER_FILE},+ {"dir", GOPHER_DIR},+ {"phonebook", GOPHER_PHONEBOOK},+ {"error", GOPHER_ERR},+ /* {MAC,UNIX,DOS}-FILE */+ {"search", GOPHER_SEARCH},+ {"telnet", GOPHER_TELNET},+ {"bin", GOPHER_BIN},+ /* REDUNDANT, TN3270 */
+ {"gif", GOPHER_GIF},+ {"img", GOPHER_IMAGE},+
+ {nil, 0},+ };
+
+ for(int c = 0; ops[c].k != nil; c++)
+ if(strcmp(ops[c].k, cmd) == 0)
+ return ops[c].op;
+
+ if(strlen(cmd) == 1)
+ return *cmd;
+
+ return -1;
+}
+
+/*
+ * Like %s, but replaces tabs with spacetab and interpolates variables
+ */
+#pragma varargck type "G" char*
+int
+gopherfmt(Fmt *fmt)
+{+ String *scratch;
+ char *str, *buf;
+ int ret = 0;
+
+ scratch = s_new();
+ str = va_arg(fmt->args, char*);
+ for(; ret >= 0 && *str != '\0'; str++)
+ switch(*str)
+ {+ case '\t':
+ ret = fmtprint(fmt, "%s", spacetab);
+ break;
+ case '$':
+ s_restart(scratch);
+
+ str++;
+ while(!isspace(*str))
+ s_putc(scratch, *str++);
+ str--;
+ s_terminate(scratch);
+
+ buf = getenv(s_to_c(scratch));
+ if(buf == nil)
+ fprint(2, "getenv: %r\n");
+ else
+ {+ /* FIXME using %G here resolves variables recursively */
+ fmtprint(fmt, "%G", buf);
+ free(buf);
+ }
+
+ break;
+ case '\\':
+ /* FIXME cannot literally print \$ with this */
+ if(*(str+1) == '$')
+ str++;
+ default:
+ fmtprint(fmt, "%c", *str);
+ }
+
+ s_free(scratch);
+ return ret;
+}
+
+#pragma varargck argpos info 1
+int
+info(char *fmt, ...)
+{+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ fmt = smprint("i%G\t\t%s\r\n", fmt, stubhost);+ n = vfprint(1, fmt, args);
+ free(fmt);
+ va_end(args);
+
+ return n;
+}
+
+#pragma varargck argpos info 1
+int
+error(char *fmt, ...)
+{+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ fmt = smprint("3%G\t\t%s\r\n", fmt, stubhost);+ n = vfprint(1, fmt, args);
+ free(fmt);
+ va_end(args);
+
+ return n;
+}
+
+int
+entry(char type, char *name, char *path, char *host, int port)
+{+ return print("%c%G\t%G\t%G\t%d\r\n", type, name, path, host, port);+}
+
+/*
+ * FIXME we should timeout at some point, so a user can't take us down
+ * by opening connections but never sending a CRLF
+ */
+String*
+readrequest(void)
+{+ String *str = s_new();
+ int c;
+ while((c = getchar()) != EOF)
+ {+ s_putc(str, c);
+ if(str->ptr[-2] == '\r' && str->ptr[-1] == '\n')
+ break;
+ }
+ str->ptr -= 2;
+ s_terminate(str);
+ return str;
+}
+
+/* TODO canonize path */
+char*
+parsepath(char* req)
+{+ if(strlen(req) == 0)
+ return strdup("/");+ return strdup(req);
+}
+
+void
+servedir(char *path)
+{+ Dir *dir;
+ long n;
+ int fd;
+
+ dir = dirstat(path);
+ if(dir == nil)
+ {+ error:
+ info("Error: %r");+ entry(GOPHER_DIR, "/", "", "neptune.shrine", 70);
+ return;
+ }
+ if(dir->qid.type&QTDIR)
+ {+ free(dir);
+ info("%G", path);+ fd = open(path, OREAD);
+ if(fd < 0)
+ goto error;
+ n = dirreadall(fd, &dir);
+ if(n < 0)
+ goto error;
+ for(int c = 0; c < n; c++)
+ {+ char *fpath = smprint("%s/%s", path, dir[c].name);+ char type = dir[c].mode&DMDIR ? GOPHER_DIR : GOPHER_FILE;
+ entry(type, dir[c].name, fpath, "neptune.shrine", 70);
+ free(fpath);
+ }
+ }
+ else
+ info("not supported...");+}
+
+String*
+getprog(char *path)
+{+ /* FIXME actually read index.waffle */
+ return s_copy(defprog);
+}
+
+String*
+nextcomm(String *prog)
+{+ String *comm;
+
+ while(isspace(*prog->ptr))
+ prog->ptr++;
+
+ if(*prog->ptr == '\0')
+ return nil;
+
+ comm = s_new();
+
+ for(; *prog->ptr != '\0'; prog->ptr++)
+ {+ if(*prog->ptr == '\n')
+ break;
+ s_putc(comm, *prog->ptr);
+ }
+
+ s_terminate(comm);
+ s_restart(comm);
+
+ return comm;
+}
+
+void
+interprog(String *prog)
+{+ int op;
+ String *line, *curtok;
+ s_restart(prog);
+
+ curtok = s_new();
+ while((line = nextcomm(prog)) != nil)
+ {+ s_parse(line, curtok);
+ switch(op = opforcmd(s_to_c(curtok)))
+ {+ case OP_COMMENT:
+ break;
+ case OP_LSDIR:
+ servedir("/usr/tevo");+ break;
+ case GOPHER_INFO:
+ info("%G", line->ptr);+ break;
+ case GOPHER_ERR:
+ error("%G", line->ptr);+ break;
+ case -1:
+ default:
+ error("command not implemented: %G", s_to_c(curtok));+ }
+ s_reset(curtok);
+ s_free(line);
+ }
+ s_free(curtok);
+}
+
+void
+main(int argc, char **argv)
+{+ String *req, *prog;
+ char *path;
+
+ ARGBEGIN {+ case 'r':
+ srvroot = EARGF(usage());
+ break;
+ case 'd':
+ fprint(2, "defprog: not implemented\n");
+ exits("noimpl");+ default:
+ usage();
+ } ARGEND;
+
+ fmtinstall('G', gopherfmt);+
+ req = readrequest();
+ path = parsepath(s_to_c(req));
+ if(path == nil)
+ {+ error("cannot parse request: %r");+ goto end;
+ }
+ prog = getprog(path);
+ if(prog == nil)
+ {+ error("cannot find index for %s: %r", path);+ goto end;
+ }
+ interprog(prog);
+
+// servedir(path);
+// info("-----");+// info("waffle on %s (plan9/%s)", getenv("sysname"), getenv("cputype"));+end:
+ print(".");+ /*
+ * we're quitting, don't think it would be bad to just leave
+ * those dangling for the os to cleanup
+ */
+ s_free(req);
+ free(path);
+ exits(0);
+}
--
⑨