man2html.c 9.3 KB

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