xref: /plan9/sys/src/cmd/ip/httpd/man2html.c (revision 0b9a5132554aff81f12a30adb0ffd48684252652)
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