man2html.c 8.9 KB

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