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