#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>

#include "common.h"

#define TIMEOUTMS 10000
#define URICAPACITY 256

char *root = nil;

void
fail(char *error)
{
	putenv("error", error);
	runcmd();
	exits(0);
}

#define failnotfound()             fail("notfound")
#define failuritoolong()           fail("uritoolong")
#define failmissingtrailingslash() fail("notrailingslash");

void
notehandler(void *, char *note)
{
	if (strcmp(note, "alarm") != 0) {
		noted(NDFLT);
	}
	exits(0);
}

Biobuf bp;

char
next1(void)
{
	char c;
	if ((c = Bgetc(&bp)) < 0) sysfatal("read");
	alarm(TIMEOUTMS);
	return c;
}

long
readuri(char *uri, long maxlen)
{
	char c;
	long len = 0;
	while ((c = next1()) != '\r' || (c = next1()) != '\n') {
		if (len == maxlen) failuritoolong();
		if (len == 0 && c != '/') failnotfound();
		*uri++ = c;
		len++;
	}
	return len;
}

int
urifmt(Fmt *f)
{
	char *s, c;
	int i, res;
	s = va_arg(f->args, char *);
	assert(f->flags & FmtPrec);
	for (i = f->prec; i > 0; --i) {
		c = *s++;
		res = isgraph(c) ? fmtprint(f, "%c", c) : fmtprint(f, "%%%02hhx", c);
		if (res < 0) return res;
	}
	return 0;
}

void
servefd(int fd)
{
	printfile(fd);
}

void
servecwd(char *prefix)
{
	putenv("prefix", prefix);
	runcmd();
}

void
usage(void)
{
	fprint(2, "usage: %s [-r remote] cmd [args...]\n", argv0);
	exits("usage");
}

void
main(int argc, char *argv[])
{
	char uri[URICAPACITY + 1];
	long urilen;
	int fd, canonical, verbose;
	Dir *dir;
	char *remote;
	verbose = 0;
	remote = nil;
	ARGBEGIN {
	case 'r':
		root = EARGF(usage());
		break;
	case 'R':
		remote = EARGF(usage());
		break;
	case 'v':
		verbose = 1;
		break;
	default:
		usage();
	} ARGEND;
	if (argc < 1) usage();
	if (root && chdir(root) < 0) sysfatal("chdir %s: %r", root);
	setcmd(*argv, argv);
	notify(notehandler);
	fmtinstall('u', urifmt);
	Binit(&bp, 0, OREAD);
	alarm(TIMEOUTMS);
	urilen = readuri(uri, URICAPACITY);
	alarm(0);
	if (verbose) {
		if (remote) syslog(0, "dratva", "Gopher   	%s	%.*u", remote, urilen, uri);
		else        syslog(0, "dratva", "Gopher   	%.*u", urilen, uri);
	}
	if ((fd = traverse(uri, &urilen, &canonical)) < 0) failnotfound();
	if (!canonical) failmissingtrailingslash();
	if (!(dir = dirfstat(fd))) sysfatal("stat: %r");
	uri[urilen] = '\0';
	if (dir->mode & DMDIR) servecwd(uri); else servefd(fd);
	exits(0);
}
