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 strlwr(char *p) 166 { 167 for(; *p; p++) 168 if('A' <= *p && *p <= 'Z') 169 *p += 'a'-'A'; 170 } 171 172 void 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 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 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 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 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