/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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); voiderror(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;}; voidlookup(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); }} voidmanindex(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");} voidman(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");} voidstrlwr(char *p){ for(; *p; p++) if('A' <= *p && *p <= 'Z') *p += 'a'-'A';} voidredirectto(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);} voidsearchfor(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 */voiddosearch(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 */voiddoconvert(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); }} voidmain(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);}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#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);
}