diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /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-x | sys/src/cmd/ip/httpd/man2html.c | 449 |
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); +} |