#include #include #include #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, "%s\n", title); hprint(hout, "

%s

\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, "plan 9 section index"); if(sect) hprint(hout, "(%d)\n", sect); hprint(hout, "\n"); hprint(hout, "
Section Index"); if(sect) hprint(hout, "(%d)\n", sect); hprint(hout, "
\n"); if(sect) hprint(hout, "

/plan9/man%d.html\n", sect, sect); else for(i = 1; i < 10; i++) hprint(hout, "

/plan9/man%d.html\n", i, i); hprint(hout, "\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, "plan 9 man %H", o); if(sect) hprint(hout, "(%d)\n", sect); hprint(hout, "\n"); hprint(hout, "

Search for %H", o); if(sect) hprint(hout, "(%d)\n", sect); hprint(hout, "
\n"); for(; list; list = list->next) hprint(hout, "

/magic/man2html%H\n", list->file, list->file); hprint(hout, "\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 here.

\r\n", uri); } void searchfor(char *search) { int i, j, n, fd; char *p, *sp; Biobufhdr *b; char *arg[32]; hprint(hout, "plan 9 search for %H\n", search); hprint(hout, "\n"); hprint(hout, "

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, "

\n"); hprint(hout, "\n"); hprint(hout, "\n"); hprint(hout, "
\n"); hprint(hout, "
Search for %H
\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, "error: No Plan 9 search index\n"); hprint(hout, ""); 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, "

/magic/man2html/%H\n", sp, sp); } hprint(hout, ""); 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, "illegal search\n"); hprint(hout, "

Illegally formatted Plan 9 man page search

\n"); search = hurlunesc(connect, search); hprint(hout, "

%H

\n", search); hprint(hout, ""); } /* * 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 | 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); }