shithub: waffle

Download patch

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);
+}