summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip/httpd/man2html.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ip/httpd/man2html.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ip/httpd/man2html.c')
-rwxr-xr-xsys/src/cmd/ip/httpd/man2html.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/httpd/man2html.c b/sys/src/cmd/ip/httpd/man2html.c
new file mode 100755
index 000000000..7971bf3bf
--- /dev/null
+++ b/sys/src/cmd/ip/httpd/man2html.c
@@ -0,0 +1,449 @@
+#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);
+}