1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "httpd.h"
5 #include "httpsrv.h"
6
7 static Hio *hout;
8 static Hio houtb;
9 static HConnect *connect;
10
11 void doconvert(char*, int);
12
13 void
error(char * title,char * fmt,...)14 error(char *title, char *fmt, ...)
15 {
16 va_list arg;
17 char buf[1024], *out;
18
19 va_start(arg, fmt);
20 out = vseprint(buf, buf+sizeof(buf), fmt, arg);
21 va_end(arg);
22 *out = 0;
23
24 hprint(hout, "%s 404 %s\n", hversion, title);
25 hprint(hout, "Date: %D\n", time(nil));
26 hprint(hout, "Server: Plan9\n");
27 hprint(hout, "Content-type: text/html\n");
28 hprint(hout, "\n");
29 hprint(hout, "<head><title>%s</title></head>\n", title);
30 hprint(hout, "<body><h1>%s</h1></body>\n", title);
31 hprint(hout, "%s\n", buf);
32 hflush(hout);
33 writelog(connect, "Reply: 404\nReason: %s\n", title);
34 exits(nil);
35 }
36
37 typedef struct Hit Hit;
38 struct Hit
39 {
40 Hit *next;
41 char *file;
42 };
43
44 void
lookup(char * object,int section,Hit ** list)45 lookup(char *object, int section, Hit **list)
46 {
47 int fd;
48 char *p, *f;
49 Biobuf b;
50 char file[256];
51 Hit *h;
52
53 while(*list != nil)
54 list = &(*list)->next;
55
56 snprint(file, sizeof(file), "/sys/man/%d/INDEX", section);
57 fd = open(file, OREAD);
58 if(fd > 0){
59 Binit(&b, fd, OREAD);
60 for(;;){
61 p = Brdline(&b, '\n');
62 if(p == nil)
63 break;
64 p[Blinelen(&b)-1] = 0;
65 f = strchr(p, ' ');
66 if(f == nil)
67 continue;
68 *f++ = 0;
69 if(strcmp(p, object) == 0){
70 h = ezalloc(sizeof *h);
71 *list = h;
72 h->next = nil;
73 snprint(file, sizeof(file), "/%d/%s", section, f);
74 h->file = estrdup(file);
75 close(fd);
76 return;
77 }
78 }
79 close(fd);
80 }
81 snprint(file, sizeof(file), "/sys/man/%d/%s", section, object);
82 if(access(file, 0) == 0){
83 h = ezalloc(sizeof *h);
84 *list = h;
85 h->next = nil;
86 h->file = estrdup(file+8);
87 }
88 }
89
90 void
manindex(int sect,int vermaj)91 manindex(int sect, int vermaj)
92 {
93 int i;
94
95 if(vermaj){
96 hokheaders(connect);
97 hprint(hout, "Content-type: text/html\r\n");
98 hprint(hout, "\r\n");
99 }
100
101 hprint(hout, "<head><title>plan 9 section index");
102 if(sect)
103 hprint(hout, "(%d)\n", sect);
104 hprint(hout, "</title></head><body>\n");
105 hprint(hout, "<H6>Section Index");
106 if(sect)
107 hprint(hout, "(%d)\n", sect);
108 hprint(hout, "</H6>\n");
109
110 if(sect)
111 hprint(hout, "<p><a href=\"/plan9/man%d.html\">/plan9/man%d.html</a>\n",
112 sect, sect);
113 else for(i = 1; i < 10; i++)
114 hprint(hout, "<p><a href=\"/plan9/man%d.html\">/plan9/man%d.html</a>\n",
115 i, i);
116 hprint(hout, "</body>\n");
117 }
118
119 void
man(char * o,int sect,int vermaj)120 man(char *o, int sect, int vermaj)
121 {
122 int i;
123 Hit *list;
124
125 list = nil;
126
127 if(*o == 0){
128 manindex(sect, vermaj);
129 return;
130 }
131
132 if(sect > 0 && sect < 10)
133 lookup(o, sect, &list);
134 else
135 for(i = 1; i < 9; i++)
136 lookup(o, i, &list);
137
138 if(list != nil && list->next == nil){
139 doconvert(list->file, vermaj);
140 return;
141 }
142
143 if(vermaj){
144 hokheaders(connect);
145 hprint(hout, "Content-type: text/html\r\n");
146 hprint(hout, "\r\n");
147 }
148
149 hprint(hout, "<head><title>plan 9 man %H", o);
150 if(sect)
151 hprint(hout, "(%d)\n", sect);
152 hprint(hout, "</title></head><body>\n");
153 hprint(hout, "<H6>Search for %H", o);
154 if(sect)
155 hprint(hout, "(%d)\n", sect);
156 hprint(hout, "</H6>\n");
157
158 for(; list; list = list->next)
159 hprint(hout, "<p><a href=\"/magic/man2html%U\">/magic/man2html%H</a>\n",
160 list->file, list->file);
161 hprint(hout, "</body>\n");
162 }
163
164 void
strlwr(char * p)165 strlwr(char *p)
166 {
167 for(; *p; p++)
168 if('A' <= *p && *p <= 'Z')
169 *p += 'a'-'A';
170 }
171
172 void
redirectto(char * uri)173 redirectto(char *uri)
174 {
175 if(connect){
176 hmoved(connect, uri);
177 exits(0);
178 }else
179 hprint(hout, "Your selection moved to <a href=\"%U\"> here</a>.<p></body>\r\n", uri);
180 }
181
182 void
searchfor(char * search)183 searchfor(char *search)
184 {
185 int i, j, n, fd;
186 char *p, *sp;
187 Biobufhdr *b;
188 char *arg[32];
189
190 hprint(hout, "<head><title>plan 9 search for %H</title></head>\n", search);
191 hprint(hout, "<body>\n");
192
193 hprint(hout, "<p>This is a keyword search through Plan 9 man pages.\n");
194 hprint(hout, "The search is case insensitive; blanks denote \"boolean and\".\n");
195 hprint(hout, "<FORM METHOD=\"GET\" ACTION=\"/magic/man2html\">\n");
196 hprint(hout, "<INPUT NAME=\"pat\" TYPE=\"text\" SIZE=\"60\">\n");
197 hprint(hout, "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n");
198 hprint(hout, "</FORM>\n");
199
200 hprint(hout, "<hr><H6>Search for %H</H6>\n", search);
201 n = getfields(search, arg, 32, 1, "+");
202 for(i = 0; i < n; i++){
203 for(j = i+1; j < n; j++){
204 if(strcmp(arg[i], arg[j]) > 0){
205 sp = arg[j];
206 arg[j] = arg[i];
207 arg[i] = sp;
208 }
209 }
210 sp = malloc(strlen(arg[i]) + 2);
211 if(sp != nil){
212 strcpy(sp+1, arg[i]);
213 sp[0] = ' ';
214 arg[i] = sp;
215 }
216 }
217
218 /*
219 * search index till line starts alphabetically < first token
220 */
221 fd = open("/sys/man/searchindex", OREAD);
222 if(fd < 0){
223 hprint(hout, "<body>error: No Plan 9 search index\n");
224 hprint(hout, "</body>");
225 return;
226 }
227 p = malloc(32*1024);
228 if(p == nil){
229 close(fd);
230 return;
231 }
232 b = ezalloc(sizeof *b);
233 Binits(b, fd, OREAD, (uchar*)p, 32*1024);
234 for(;;){
235 p = Brdline(b, '\n');
236 if(p == nil)
237 break;
238 p[Blinelen(b)-1] = 0;
239 for(i = 0; i < n; i++){
240 sp = strstr(p, arg[i]);
241 if(sp == nil)
242 break;
243 p = sp;
244 }
245 if(i < n)
246 continue;
247 sp = strrchr(p, '\t');
248 if(sp == nil)
249 continue;
250 sp++;
251 hprint(hout, "<p><a href=\"/magic/man2html/%U\">/magic/man2html/%H</a>\n",
252 sp, sp);
253 }
254 hprint(hout, "</body>");
255
256 Bterm(b);
257 free(b);
258 free(p);
259 close(fd);
260 }
261
262 /*
263 * find man pages mentioning the search string
264 */
265 void
dosearch(int vermaj,char * search)266 dosearch(int vermaj, char *search)
267 {
268 int sect;
269 char *p;
270
271 if(strncmp(search, "man=", 4) == 0){
272 sect = 0;
273 search = hurlunesc(connect, search+4);
274 p = strchr(search, '&');
275 if(p != nil){
276 *p++ = 0;
277 if(strncmp(p, "sect=", 5) == 0)
278 sect = atoi(p+5);
279 }
280 man(search, sect, vermaj);
281 return;
282 }
283
284 if(vermaj){
285 hokheaders(connect);
286 hprint(hout, "Content-type: text/html\r\n");
287 hprint(hout, "\r\n");
288 }
289
290 if(strncmp(search, "pat=", 4) == 0){
291 search = hurlunesc(connect, search+4);
292 search = hlower(search);
293 searchfor(search);
294 return;
295 }
296
297 hprint(hout, "<head><title>illegal search</title></head>\n");
298 hprint(hout, "<body><p>Illegally formatted Plan 9 man page search</p>\n");
299 search = hurlunesc(connect, search);
300 hprint(hout, "<body><p>%H</p>\n", search);
301 hprint(hout, "</body>");
302 }
303
304 /*
305 * convert a man page to html and output
306 */
307 void
doconvert(char * uri,int vermaj)308 doconvert(char *uri, int vermaj)
309 {
310 char *p;
311 char file[256];
312 char title[256];
313 char err[ERRMAX];
314 int pfd[2];
315 Dir *d;
316 Waitmsg *w;
317 int x;
318
319 if(strstr(uri, ".."))
320 error("bad URI", "man page URI cannot contain ..");
321 p = strstr(uri, "/intro");
322
323 if(p == nil){
324 while(*uri == '/')
325 uri++;
326 /* redirect section requests */
327 snprint(file, sizeof(file), "/sys/man/%s", uri);
328 d = dirstat(file);
329 if(d == nil){
330 strlwr(file);
331 if(dirstat(file) != nil){
332 snprint(file, sizeof(file), "/magic/man2html/%s", uri);
333 strlwr(file);
334 redirectto(file);
335 }
336 error(uri, "man page not found");
337 }
338 x = d->qid.type;
339 free(d);
340 if(x & QTDIR){
341 if(*uri == 0 || strcmp(uri, "/") == 0)
342 redirectto("/sys/man/index.html");
343 else {
344 snprint(file, sizeof(file), "/sys/man/%s/INDEX.html",
345 uri+1);
346 redirectto(file);
347 }
348 return;
349 }
350 } else {
351 /* rewrite the name intro */
352 *p = 0;
353 snprint(file, sizeof(file), "/sys/man/%s/0intro", uri);
354 d = dirstat(file);
355 free(d);
356 if(d == nil)
357 error(uri, "man page not found");
358 }
359
360 if(vermaj){
361 hokheaders(connect);
362 hprint(hout, "Content-type: text/html\r\n");
363 hprint(hout, "\r\n");
364 }
365 hflush(hout);
366
367 if(pipe(pfd) < 0)
368 error("out of resources", "pipe failed");
369
370 /* troff -manhtml <file> | troff2html -t '' */
371 switch(fork()){
372 case -1:
373 error("out of resources", "fork failed");
374 case 0:
375 snprint(title, sizeof(title), "Plan 9 %s", file);
376 close(0);
377 dup(pfd[0], 0);
378 close(pfd[0]);
379 close(pfd[1]);
380 execl("/bin/troff2html", "troff2html", "-t", title, nil);
381 errstr(err, sizeof err);
382 exits(err);
383 }
384 switch(fork()){
385 case -1:
386 error("out of resources", "fork failed");
387 case 0:
388 snprint(title, sizeof(title), "Plan 9 %s", file);
389 close(0);
390 close(1);
391 dup(pfd[1], 1);
392 close(pfd[0]);
393 close(pfd[1]);
394 execl("/bin/troff", "troff", "-manhtml", file, nil);
395 errstr(err, sizeof err);
396 exits(err);
397 }
398 close(pfd[0]);
399 close(pfd[1]);
400
401 /* wait for completion */
402 for(;;){
403 w = wait();
404 if(w == nil)
405 break;
406 if(w->msg[0] != 0)
407 print("whoops %s\n", w->msg);
408 free(w);
409 }
410 }
411
412 void
main(int argc,char ** argv)413 main(int argc, char **argv)
414 {
415 fmtinstall('H', httpfmt);
416 fmtinstall('U', hurlfmt);
417
418 if(argc == 2){
419 hinit(&houtb, 1, Hwrite);
420 hout = &houtb;
421 doconvert(argv[1], 0);
422 exits(nil);
423 }
424 close(2);
425
426 connect = init(argc, argv);
427 hout = &connect->hout;
428 if(hparseheaders(connect, HSTIMEOUT) < 0)
429 exits("failed");
430
431 if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){
432 hunallowed(connect, "GET, HEAD");
433 exits("not allowed");
434 }
435 if(connect->head.expectother || connect->head.expectcont){
436 hfail(connect, HExpectFail, nil);
437 exits("failed");
438 }
439
440 bind("/usr/web/sys/man", "/sys/man", MREPL);
441
442 if(connect->req.search != nil)
443 dosearch(connect->req.vermaj, connect->req.search);
444 else
445 doconvert(connect->req.uri, connect->req.vermaj);
446 hflush(hout);
447 writelog(connect, "200 man2html %ld %ld\n", hout->seek, hout->seek);
448 exits(nil);
449 }
450