#include <u.h>
#include <libc.h>
#include <bio.h>
#include "httpd.h"
#include "httpsrv.h"

static	Hio		*hout;
static	Hio		houtb;
static	HConnect	*connect;

void	doconvert(char*, int);

void
error(char *title, char *fmt, ...)
{
	va_list arg;
	char buf[1024], *out;

	va_start(arg, fmt);
	out = vseprint(buf, buf+sizeof(buf), fmt, arg);
	va_end(arg);
	*out = 0;

	hprint(hout, "%s 404 %s\n", hversion, title);
	hprint(hout, "Date: %D\n", time(nil));
	hprint(hout, "Server: Plan9\n");
	hprint(hout, "Content-type: text/html\n");
	hprint(hout, "\n");
	hprint(hout, "<head><title>%s</title></head>\n", title);
	hprint(hout, "<body><h1>%s</h1></body>\n", title);
	hprint(hout, "%s\n", buf);
	hflush(hout);
	writelog(connect, "Reply: 404\nReason: %s\n", title);
	exits(nil);
}

typedef struct Hit	Hit;
struct Hit 
{
	Hit *next;
	char *file;
};

void
lookup(char *object, int section, Hit **list)
{
	int fd;
	char *p, *f;
	Biobuf b;
	char file[256];
	Hit *h;

	while(*list != nil)
		list = &(*list)->next;

	snprint(file, sizeof(file), "/sys/man/%d/INDEX", section);
	fd = open(file, OREAD);
	if(fd >= 0){
		Binit(&b, fd, OREAD);
		for(;;){
			p = Brdline(&b, '\n');
			if(p == nil)
				break;
			p[Blinelen(&b)-1] = 0;
			f = strchr(p, ' ');
			if(f == nil)
				continue;
			*f++ = 0;
			if(strcmp(p, object) == 0){
				h = ezalloc(sizeof *h);
				*list = h;
				h->next = nil;
				snprint(file, sizeof(file), "/%d/%s", section, f);
				h->file = estrdup(file);
				close(fd);
				return;
			}
		}
		close(fd);
	}
	snprint(file, sizeof(file), "/sys/man/%d/%s", section, object);
	if(access(file, 0) == 0){
		h = ezalloc(sizeof *h);
		*list = h;
		h->next = nil;
		h->file = estrdup(file+8);
	}
}

void
manindex(int sect, int vermaj)
{
	int i;

	if(vermaj){
		hokheaders(connect);
		hprint(hout, "Content-type: text/html\r\n");
		hprint(hout, "\r\n");
	}

	hprint(hout, "<head><title>plan 9 section index");
	if(sect)
		hprint(hout, "(%d)\n", sect);
	hprint(hout, "</title></head><body>\n");
	hprint(hout, "<H6>Section Index");
	if(sect)
		hprint(hout, "(%d)\n", sect);
	hprint(hout, "</H6>\n");

	if(sect)
		hprint(hout, "<p><a href=\"/plan9/man%d.html\">/plan9/man%d.html</a>\n",
			sect, sect);
	else for(i = 1; i < 10; i++)
		hprint(hout, "<p><a href=\"/plan9/man%d.html\">/plan9/man%d.html</a>\n",
			i, i);
	hprint(hout, "</body>\n");
}

void
man(char *o, int sect, int vermaj)
{
	int i;
	Hit *list;

	list = nil;

	if(*o == 0){
		manindex(sect, vermaj);
		return;
	}

	if(sect > 0 && sect < 10)
		lookup(o, sect, &list);
	else
		for(i = 1; i < 9; i++)
			lookup(o, i, &list);

	if(list != nil && list->next == nil){
		doconvert(list->file, vermaj);
		return;
	}

	if(vermaj){
		hokheaders(connect);
		hprint(hout, "Content-type: text/html\r\n");
		hprint(hout, "\r\n");
	}

	hprint(hout, "<head><title>plan 9 man %H", o);
	if(sect)
		hprint(hout, "(%d)\n", sect);
	hprint(hout, "</title></head><body>\n");
	hprint(hout, "<H6>Search for %H", o);
	if(sect)
		hprint(hout, "(%d)\n", sect);
	hprint(hout, "</H6>\n");

	for(; list; list = list->next)
		hprint(hout, "<p><a href=\"/magic/man2html%U\">/magic/man2html%H</a>\n",
			list->file, list->file);
	hprint(hout, "</body>\n");
}

void
strlwr(char *p)
{
	for(; *p; p++)
		if('A' <= *p && *p <= 'Z')
			*p += 'a'-'A';
}

void
redirectto(char *uri)
{
	if(connect){
		hmoved(connect, uri);
		exits(0);
	}else
		hprint(hout, "Your selection moved to <a href=\"%U\"> here</a>.<p></body>\r\n", uri);
}

void
searchfor(char *search)
{
	int i, j, n, fd;
	char *p, *sp;
	Biobufhdr *b;
	char *arg[32];

	hprint(hout, "<head><title>plan 9 search for %H</title></head>\n", search);
	hprint(hout, "<body>\n");

	hprint(hout, "<p>This is a keyword search through Plan 9 man pages.\n");
	hprint(hout, "The search is case insensitive; blanks denote \"boolean and\".\n");
	hprint(hout, "<FORM METHOD=\"GET\" ACTION=\"/magic/man2html\">\n");
	hprint(hout, "<INPUT NAME=\"pat\" TYPE=\"text\" SIZE=\"60\">\n");
	hprint(hout, "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n");
	hprint(hout, "</FORM>\n");

	hprint(hout, "<hr><H6>Search for %H</H6>\n", search);
	n = getfields(search, arg, 32, 1, "+");
	for(i = 0; i < n; i++){
		for(j = i+1; j < n; j++){
			if(strcmp(arg[i], arg[j]) > 0){
				sp = arg[j];
				arg[j] = arg[i];
				arg[i] = sp;
			}
		}
		sp = malloc(strlen(arg[i]) + 2);
		if(sp != nil){
			strcpy(sp+1, arg[i]);
			sp[0] = ' ';
			arg[i] = sp;
		}
	}

	/*
	 *  search index till line starts alphabetically < first token
	 */
	fd = open("/sys/man/searchindex", OREAD);
	if(fd < 0){
		hprint(hout, "<body>error: No Plan 9 search index\n");
		hprint(hout, "</body>");
		return;
	}
	p = malloc(32*1024);
	if(p == nil){
		close(fd);
		return;
	}
	b = ezalloc(sizeof *b);
	Binits(b, fd, OREAD, (uchar*)p, 32*1024);
	for(;;){
		p = Brdline(b, '\n');
		if(p == nil)
			break;
		p[Blinelen(b)-1] = 0;
		for(i = 0; i < n; i++){
			sp = strstr(p, arg[i]);
			if(sp == nil)
				break;
			p = sp;
		}
		if(i < n)
			continue;
		sp = strrchr(p, '\t');
		if(sp == nil)
			continue;
		sp++;
		hprint(hout, "<p><a href=\"/magic/man2html/%U\">/magic/man2html/%H</a>\n",
			sp, sp);
	}
	hprint(hout, "</body>");

	Bterm(b);
	free(b);
	free(p);
	close(fd);
}

/*
 *  find man pages mentioning the search string
 */
void
dosearch(int vermaj, char *search)
{
	int sect;
	char *p;

	if(strncmp(search, "man=", 4) == 0){
		sect = 0;
		search = hurlunesc(connect, search+4);
		p = strchr(search, '&');
		if(p != nil){
			*p++ = 0;
			if(strncmp(p, "sect=", 5) == 0)
				sect = atoi(p+5);
		}
		man(search, sect, vermaj);
		return;
	}

	if(vermaj){
		hokheaders(connect);
		hprint(hout, "Content-type: text/html\r\n");
		hprint(hout, "\r\n");
	}

	if(strncmp(search, "pat=", 4) == 0){
		search = hurlunesc(connect, search+4);
		search = hlower(search);
		searchfor(search);
		return;
	}

	hprint(hout, "<head><title>illegal search</title></head>\n");
	hprint(hout, "<body><p>Illegally formatted Plan 9 man page search</p>\n");
	search = hurlunesc(connect, search);
	hprint(hout, "<body><p>%H</p>\n", search);
	hprint(hout, "</body>");
}

/*
 *  convert a man page to html and output
 */
void
doconvert(char *uri, int vermaj)
{
	char *p;
	char file[256];
	char title[256];
	char err[ERRMAX];
	int pfd[2];
	Dir *d;
	Waitmsg *w;
	int x;

	if(strstr(uri, ".."))
		error("bad URI", "man page URI cannot contain ..");
	p = strstr(uri, "/intro");

	if(p == nil){
		while(*uri == '/')
			uri++;
		/* redirect section requests */
		snprint(file, sizeof(file), "/sys/man/%s", uri);
		d = dirstat(file);
		if(d == nil){
			strlwr(file);
			if(dirstat(file) != nil){
				snprint(file, sizeof(file), "/magic/man2html/%s", uri);
				strlwr(file);
				redirectto(file);
			}
			error(uri, "man page not found");
		}
		x = d->qid.type;
		free(d);
		if(x & QTDIR){
			if(*uri == 0 || strcmp(uri, "/") == 0)
				redirectto("/sys/man/index.html");
			else {
				snprint(file, sizeof(file), "/sys/man/%s/INDEX.html",
					uri+1);
				redirectto(file);
			}
			return;
		}
	} else {
		/* rewrite the name intro */
		*p = 0;
		snprint(file, sizeof(file), "/sys/man/%s/0intro", uri);
		d = dirstat(file);
		free(d);
		if(d == nil)
			error(uri, "man page not found");
	}

	if(vermaj){
		hokheaders(connect);
		hprint(hout, "Content-type: text/html\r\n");
		hprint(hout, "\r\n");
	}
	hflush(hout);

	if(pipe(pfd) < 0)
		error("out of resources", "pipe failed");

	/* troff -manhtml <file> | troff2html -t '' */
	switch(fork()){
	case -1:
		error("out of resources", "fork failed");
	case 0:
		snprint(title, sizeof(title), "Plan 9 %s", file);
		close(0);
		dup(pfd[0], 0);
		close(pfd[0]);
		close(pfd[1]);
		execl("/bin/troff2html", "troff2html", "-t", title, nil);
		errstr(err, sizeof err);
		exits(err);
	}
	switch(fork()){
	case -1:
		error("out of resources", "fork failed");
	case 0:
		snprint(title, sizeof(title), "Plan 9 %s", file);
		close(0);
		close(1);
		dup(pfd[1], 1);
		close(pfd[0]);
		close(pfd[1]);
		execl("/bin/troff", "troff", "-manhtml", file, nil);
		errstr(err, sizeof err);
		exits(err);
	}
	close(pfd[0]);
	close(pfd[1]);

	/* wait for completion */
	for(;;){
		w = wait();
		if(w == nil)
			break;
		if(w->msg[0] != 0)
			print("whoops %s\n", w->msg);
		free(w);
	}
}

void
main(int argc, char **argv)
{
	fmtinstall('H', httpfmt);
	fmtinstall('U', hurlfmt);

	if(argc == 2){
		hinit(&houtb, 1, Hwrite);
		hout = &houtb;
		doconvert(argv[1], 0);
		exits(nil);
	}
	close(2);

	connect = init(argc, argv);
	hout = &connect->hout;
	if(hparseheaders(connect, HSTIMEOUT) < 0)
		exits("failed");

	if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){
		hunallowed(connect, "GET, HEAD");
		exits("not allowed");
	}
	if(connect->head.expectother || connect->head.expectcont){
		hfail(connect, HExpectFail, nil);
		exits("failed");
	}

	bind("/usr/web/sys/man", "/sys/man", MREPL);

	if(connect->req.search != nil)
		dosearch(connect->req.vermaj, connect->req.search);
	else
		doconvert(connect->req.uri, connect->req.vermaj);
	hflush(hout);
	writelog(connect, "200 man2html %ld %ld\n", hout->seek, hout->seek);
	exits(nil);
}
