man2html.c 8.6 KB

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