xref: /plan9/sys/src/cmd/ip/httpd/man2html.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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
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
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
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
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
165 redirectto(char *uri)
166 {
167 	if(connect){
168 		hmoved(connect, uri);
169 		exits(0);
170 	}else
171 		hprint(hout, "Your selection moved to <a href=\"%U\"> here</a>.<p></body>\r\n", uri);
172 }
173 
174 void
175 searchfor(char *search)
176 {
177 	int i, j, n, fd;
178 	char *p, *sp;
179 	Biobufhdr *b;
180 	char *arg[32];
181 
182 	hprint(hout, "<head><title>plan 9 search for %H</title></head>\n", search);
183 	hprint(hout, "<body>\n");
184 
185 	hprint(hout, "<p>This is a keyword search through Plan 9 man pages.\n");
186 	hprint(hout, "The search is case insensitive; blanks denote \"boolean and\".\n");
187 	hprint(hout, "<FORM METHOD=\"GET\" ACTION=\"/magic/man2html\">\n");
188 	hprint(hout, "<INPUT NAME=\"pat\" TYPE=\"text\" SIZE=\"60\">\n");
189 	hprint(hout, "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n");
190 	hprint(hout, "</FORM>\n");
191 
192 	hprint(hout, "<hr><H6>Search for %H</H6>\n", search);
193 	n = getfields(search, arg, 32, 1, "+");
194 	for(i = 0; i < n; i++){
195 		for(j = i+1; j < n; j++){
196 			if(strcmp(arg[i], arg[j]) > 0){
197 				sp = arg[j];
198 				arg[j] = arg[i];
199 				arg[i] = sp;
200 			}
201 		}
202 		sp = malloc(strlen(arg[i]) + 2);
203 		if(sp != nil){
204 			strcpy(sp+1, arg[i]);
205 			sp[0] = ' ';
206 			arg[i] = sp;
207 		}
208 	}
209 
210 	/*
211 	 *  search index till line starts alphabetically < first token
212 	 */
213 	fd = open("/sys/man/searchindex", OREAD);
214 	if(fd < 0){
215 		hprint(hout, "<body>error: No Plan 9 search index\n");
216 		hprint(hout, "</body>");
217 		return;
218 	}
219 	p = malloc(32*1024);
220 	if(p == nil){
221 		close(fd);
222 		return;
223 	}
224 	b = ezalloc(sizeof *b);
225 	Binits(b, fd, OREAD, (uchar*)p, 32*1024);
226 	for(;;){
227 		p = Brdline(b, '\n');
228 		if(p == nil)
229 			break;
230 		p[Blinelen(b)-1] = 0;
231 		for(i = 0; i < n; i++){
232 			sp = strstr(p, arg[i]);
233 			if(sp == nil)
234 				break;
235 			p = sp;
236 		}
237 		if(i < n)
238 			continue;
239 		sp = strrchr(p, '\t');
240 		if(sp == nil)
241 			continue;
242 		sp++;
243 		hprint(hout, "<p><a href=\"/magic/man2html/%U\">/magic/man2html/%H</a>\n",
244 			sp, sp);
245 	}
246 	hprint(hout, "</body>");
247 
248 	Bterm(b);
249 	free(b);
250 	free(p);
251 	close(fd);
252 }
253 
254 /*
255  *  find man pages mentioning the search string
256  */
257 void
258 dosearch(int vermaj, char *search)
259 {
260 	int sect;
261 	char *p;
262 
263 	if(strncmp(search, "man=", 4) == 0){
264 		sect = 0;
265 		search = hurlunesc(connect, search+4);
266 		p = strchr(search, '&');
267 		if(p != nil){
268 			*p++ = 0;
269 			if(strncmp(p, "sect=", 5) == 0)
270 				sect = atoi(p+5);
271 		}
272 		man(search, sect, vermaj);
273 		return;
274 	}
275 
276 	if(vermaj){
277 		hokheaders(connect);
278 		hprint(hout, "Content-type: text/html\r\n");
279 		hprint(hout, "\r\n");
280 	}
281 
282 	if(strncmp(search, "pat=", 4) == 0){
283 		search = hurlunesc(connect, search+4);
284 		search = hlower(search);
285 		searchfor(search);
286 		return;
287 	}
288 
289 	hprint(hout, "<head><title>illegal search</title></head>\n");
290 	hprint(hout, "<body><p>Illegally formatted Plan 9 man page search</p>\n");
291 	search = hurlunesc(connect, search);
292 	hprint(hout, "<body><p>%H</p>\n", search);
293 	hprint(hout, "</body>");
294 }
295 
296 /*
297  *  convert a man page to html and output
298  */
299 void
300 doconvert(char *uri, int vermaj)
301 {
302 	char *p;
303 	char file[256];
304 	char title[256];
305 	char err[ERRMAX];
306 	int pfd[2];
307 	Dir *d;
308 	Waitmsg *w;
309 	int x;
310 
311 	if(strstr(uri, ".."))
312 		error("bad URI", "man page URI cannot contain ..");
313 	p = strstr(uri, "/intro");
314 
315 	if(p == nil){
316 		/* redirect section requests */
317 		snprint(file, sizeof(file), "/sys/man/%s", uri);
318 		d = dirstat(file);
319 		if(d == nil)
320 			error(uri, "man page not found");
321 		x = d->qid.type;
322 		free(d);
323 		if(x & QTDIR){
324 			if(*uri == 0 || strcmp(uri, "/") == 0)
325 				redirectto("/sys/man/index.html");
326 			else {
327 				snprint(file, sizeof(file), "/sys/man/%s/INDEX.html",
328 					uri+1);
329 				redirectto(file);
330 			}
331 			return;
332 		}
333 	} else {
334 		/* rewrite the name intro */
335 		*p = 0;
336 		snprint(file, sizeof(file), "/sys/man/%s/0intro", uri);
337 		d = dirstat(file);
338 		free(d);
339 		if(d == nil)
340 			error(uri, "man page not found");
341 	}
342 
343 	if(vermaj){
344 		hokheaders(connect);
345 		hprint(hout, "Content-type: text/html\r\n");
346 		hprint(hout, "\r\n");
347 	}
348 	hflush(hout);
349 
350 	if(pipe(pfd) < 0)
351 		error("out of resources", "pipe failed");
352 
353 	/* troff -manhtml <file> | troff2html -t '' */
354 	switch(fork()){
355 	case -1:
356 		error("out of resources", "fork failed");
357 	case 0:
358 		snprint(title, sizeof(title), "Plan 9 %s", file);
359 		close(0);
360 		dup(pfd[0], 0);
361 		close(pfd[0]);
362 		close(pfd[1]);
363 		execl("/bin/troff2html", "troff2html", "-t", title, 0);
364 		errstr(err, sizeof err);
365 		exits(err);
366 	}
367 	switch(fork()){
368 	case -1:
369 		error("out of resources", "fork failed");
370 	case 0:
371 		snprint(title, sizeof(title), "Plan 9 %s", file);
372 		close(0);
373 		close(1);
374 		dup(pfd[1], 1);
375 		close(pfd[0]);
376 		close(pfd[1]);
377 		execl("/bin/troff", "troff", "-manhtml", file, 0);
378 		errstr(err, sizeof err);
379 		exits(err);
380 	}
381 	close(pfd[0]);
382 	close(pfd[1]);
383 
384 	/* wait for completion */
385 	for(;;){
386 		w = wait();
387 		if(w == nil)
388 			break;
389 		if(w->msg[0] != 0)
390 			print("whoops %s\n", w->msg);
391 		free(w);
392 	}
393 }
394 
395 void
396 main(int argc, char **argv)
397 {
398 	fmtinstall('H', httpfmt);
399 	fmtinstall('U', hurlfmt);
400 
401 	if(argc == 2){
402 		hinit(&houtb, 1, Hwrite);
403 		hout = &houtb;
404 		doconvert(argv[1], 0);
405 		exits(nil);
406 	}
407 	close(2);
408 
409 	connect = init(argc, argv);
410 	hout = &connect->hout;
411 	if(hparseheaders(connect, HSTIMEOUT) < 0)
412 		exits("failed");
413 
414 	if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){
415 		hunallowed(connect, "GET, HEAD");
416 		exits("not allowed");
417 	}
418 	if(connect->head.expectother || connect->head.expectcont){
419 		hfail(connect, HExpectFail, nil);
420 		exits("failed");
421 	}
422 
423 	bind("/usr/web/sys/man", "/sys/man", MREPL);
424 
425 	if(connect->req.search != nil)
426 		dosearch(connect->req.vermaj, connect->req.search);
427 	else
428 		doconvert(connect->req.uri, connect->req.vermaj);
429 	hflush(hout);
430 	writelog(connect, "200 man2html %ld %ld\n", hout->seek, hout->seek);
431 	exits(nil);
432 }
433