Annotation of src/usr.bin/mandoc/cgi.c, Revision 1.1
1.1 ! schwarze 1: /* $Id: cgi.c,v 1.61 2014/07/10 00:52:50 schwarze Exp $ */
! 2: /*
! 3: * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
! 4: * Copyright (c) 2014 Ingo Schwarze <schwarze@usta.de>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18: #include <ctype.h>
! 19: #include <errno.h>
! 20: #include <fcntl.h>
! 21: #include <limits.h>
! 22: #include <stdio.h>
! 23: #include <stdlib.h>
! 24: #include <string.h>
! 25: #include <unistd.h>
! 26:
! 27: #include "mandoc.h"
! 28: #include "mandoc_aux.h"
! 29: #include "main.h"
! 30: #include "manpath.h"
! 31: #include "mansearch.h"
! 32:
! 33: enum page {
! 34: PAGE_INDEX,
! 35: PAGE_SEARCH,
! 36: PAGE_SHOW,
! 37: PAGE__MAX
! 38: };
! 39:
! 40: /*
! 41: * A query as passed to the search function.
! 42: */
! 43: struct query {
! 44: const char *manpath; /* desired manual directory */
! 45: const char *arch; /* architecture */
! 46: const char *sec; /* manual section */
! 47: const char *expr; /* unparsed expression string */
! 48: int legacy; /* whether legacy mode */
! 49: };
! 50:
! 51: struct req {
! 52: struct query q;
! 53: char **p; /* array of available manpaths */
! 54: size_t psz; /* number of available manpaths */
! 55: enum page page;
! 56: };
! 57:
! 58: static void catman(const struct req *, const char *);
! 59: static int cmp(const void *, const void *);
! 60: static void format(const struct req *, const char *);
! 61: static void html_print(const char *);
! 62: static void html_printquery(const struct req *);
! 63: static void html_putchar(char);
! 64: static int http_decode(char *);
! 65: static void http_parse(struct req *, char *);
! 66: static void http_print(const char *);
! 67: static void http_putchar(char);
! 68: static void http_printquery(const struct req *);
! 69: static void pathgen(struct req *);
! 70: static void pg_index(const struct req *, char *);
! 71: static void pg_search(const struct req *, char *);
! 72: static void pg_show(const struct req *, char *);
! 73: static void resp_begin_html(int, const char *);
! 74: static void resp_begin_http(int, const char *);
! 75: static void resp_end_html(void);
! 76: static void resp_error_badrequest(const char *);
! 77: static void resp_error_internal(void);
! 78: static void resp_error_notfound(const char *);
! 79: static void resp_index(const struct req *);
! 80: static void resp_noresult(const struct req *,
! 81: const char *);
! 82: static void resp_search(const struct req *,
! 83: struct manpage *, size_t);
! 84: static void resp_searchform(const struct req *);
! 85:
! 86: static const char *scriptname; /* CGI script name */
! 87: static const char *mandir; /* contains all manpath directories */
! 88: static const char *cssdir; /* css directory */
! 89: static const char *httphost; /* hostname used in the URIs */
! 90:
! 91: static const char * const pages[PAGE__MAX] = {
! 92: "index", /* PAGE_INDEX */
! 93: "search", /* PAGE_SEARCH */
! 94: "show", /* PAGE_SHOW */
! 95: };
! 96:
! 97: /*
! 98: * Print a character, escaping HTML along the way.
! 99: * This will pass non-ASCII straight to output: be warned!
! 100: */
! 101: static void
! 102: html_putchar(char c)
! 103: {
! 104:
! 105: switch (c) {
! 106: case ('"'):
! 107: printf(""e;");
! 108: break;
! 109: case ('&'):
! 110: printf("&");
! 111: break;
! 112: case ('>'):
! 113: printf(">");
! 114: break;
! 115: case ('<'):
! 116: printf("<");
! 117: break;
! 118: default:
! 119: putchar((unsigned char)c);
! 120: break;
! 121: }
! 122: }
! 123:
! 124: static void
! 125: http_printquery(const struct req *req)
! 126: {
! 127:
! 128: if (NULL != req->q.manpath) {
! 129: printf("&manpath=");
! 130: http_print(req->q.manpath);
! 131: }
! 132: if (NULL != req->q.sec) {
! 133: printf("&sec=");
! 134: http_print(req->q.sec);
! 135: }
! 136: if (NULL != req->q.arch) {
! 137: printf("&arch=");
! 138: http_print(req->q.arch);
! 139: }
! 140: if (NULL != req->q.expr) {
! 141: printf("&expr=");
! 142: http_print(req->q.expr ? req->q.expr : "");
! 143: }
! 144: }
! 145:
! 146: static void
! 147: html_printquery(const struct req *req)
! 148: {
! 149:
! 150: if (NULL != req->q.manpath) {
! 151: printf("&manpath=");
! 152: html_print(req->q.manpath);
! 153: }
! 154: if (NULL != req->q.sec) {
! 155: printf("&sec=");
! 156: html_print(req->q.sec);
! 157: }
! 158: if (NULL != req->q.arch) {
! 159: printf("&arch=");
! 160: html_print(req->q.arch);
! 161: }
! 162: if (NULL != req->q.expr) {
! 163: printf("&expr=");
! 164: html_print(req->q.expr ? req->q.expr : "");
! 165: }
! 166: }
! 167:
! 168: static void
! 169: http_print(const char *p)
! 170: {
! 171:
! 172: if (NULL == p)
! 173: return;
! 174: while ('\0' != *p)
! 175: http_putchar(*p++);
! 176: }
! 177:
! 178: /*
! 179: * Call through to html_putchar().
! 180: * Accepts NULL strings.
! 181: */
! 182: static void
! 183: html_print(const char *p)
! 184: {
! 185:
! 186: if (NULL == p)
! 187: return;
! 188: while ('\0' != *p)
! 189: html_putchar(*p++);
! 190: }
! 191:
! 192: /*
! 193: * Parse out key-value pairs from an HTTP request variable.
! 194: * This can be either a cookie or a POST/GET string, although man.cgi
! 195: * uses only GET for simplicity.
! 196: */
! 197: static void
! 198: http_parse(struct req *req, char *p)
! 199: {
! 200: char *key, *val;
! 201: int legacy;
! 202:
! 203: memset(&req->q, 0, sizeof(struct query));
! 204: req->q.manpath = req->p[0];
! 205:
! 206: legacy = -1;
! 207: while ('\0' != *p) {
! 208: key = p;
! 209: val = NULL;
! 210:
! 211: p += (int)strcspn(p, ";&");
! 212: if ('\0' != *p)
! 213: *p++ = '\0';
! 214: if (NULL != (val = strchr(key, '=')))
! 215: *val++ = '\0';
! 216:
! 217: if ('\0' == *key || NULL == val || '\0' == *val)
! 218: continue;
! 219:
! 220: /* Just abort handling. */
! 221:
! 222: if ( ! http_decode(key))
! 223: break;
! 224: if (NULL != val && ! http_decode(val))
! 225: break;
! 226:
! 227: if (0 == strcmp(key, "expr"))
! 228: req->q.expr = val;
! 229: else if (0 == strcmp(key, "query"))
! 230: req->q.expr = val;
! 231: else if (0 == strcmp(key, "sec"))
! 232: req->q.sec = val;
! 233: else if (0 == strcmp(key, "sektion"))
! 234: req->q.sec = val;
! 235: else if (0 == strcmp(key, "arch"))
! 236: req->q.arch = val;
! 237: else if (0 == strcmp(key, "manpath"))
! 238: req->q.manpath = val;
! 239: else if (0 == strcmp(key, "apropos"))
! 240: legacy = 0 == strcmp(val, "0");
! 241: }
! 242:
! 243: /* Test for old man.cgi compatibility mode. */
! 244:
! 245: req->q.legacy = legacy > 0;
! 246:
! 247: /*
! 248: * Section "0" means no section when in legacy mode.
! 249: * For some man.cgi scripts, "default" arch is none.
! 250: */
! 251:
! 252: if (req->q.legacy && NULL != req->q.sec)
! 253: if (0 == strcmp(req->q.sec, "0"))
! 254: req->q.sec = NULL;
! 255: if (req->q.legacy && NULL != req->q.arch)
! 256: if (0 == strcmp(req->q.arch, "default"))
! 257: req->q.arch = NULL;
! 258: }
! 259:
! 260: static void
! 261: http_putchar(char c)
! 262: {
! 263:
! 264: if (isalnum((unsigned char)c)) {
! 265: putchar((unsigned char)c);
! 266: return;
! 267: } else if (' ' == c) {
! 268: putchar('+');
! 269: return;
! 270: }
! 271: printf("%%%.2x", c);
! 272: }
! 273:
! 274: /*
! 275: * HTTP-decode a string. The standard explanation is that this turns
! 276: * "%4e+foo" into "n foo" in the regular way. This is done in-place
! 277: * over the allocated string.
! 278: */
! 279: static int
! 280: http_decode(char *p)
! 281: {
! 282: char hex[3];
! 283: int c;
! 284:
! 285: hex[2] = '\0';
! 286:
! 287: for ( ; '\0' != *p; p++) {
! 288: if ('%' == *p) {
! 289: if ('\0' == (hex[0] = *(p + 1)))
! 290: return(0);
! 291: if ('\0' == (hex[1] = *(p + 2)))
! 292: return(0);
! 293: if (1 != sscanf(hex, "%x", &c))
! 294: return(0);
! 295: if ('\0' == c)
! 296: return(0);
! 297:
! 298: *p = (char)c;
! 299: memmove(p + 1, p + 3, strlen(p + 3) + 1);
! 300: } else
! 301: *p = '+' == *p ? ' ' : *p;
! 302: }
! 303:
! 304: *p = '\0';
! 305: return(1);
! 306: }
! 307:
! 308: static void
! 309: resp_begin_http(int code, const char *msg)
! 310: {
! 311:
! 312: if (200 != code)
! 313: printf("Status: %d %s\n", code, msg);
! 314:
! 315: puts("Content-Type: text/html; charset=utf-8\n"
! 316: "Cache-Control: no-cache\n"
! 317: "Pragma: no-cache\n"
! 318: "");
! 319:
! 320: fflush(stdout);
! 321: }
! 322:
! 323: static void
! 324: resp_begin_html(int code, const char *msg)
! 325: {
! 326:
! 327: resp_begin_http(code, msg);
! 328:
! 329: printf("<!DOCTYPE HTML PUBLIC "
! 330: " \"-//W3C//DTD HTML 4.01//EN\""
! 331: " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
! 332: "<HTML>\n"
! 333: "<HEAD>\n"
! 334: "<META HTTP-EQUIV=\"Content-Type\""
! 335: " CONTENT=\"text/html; charset=utf-8\">\n"
! 336: "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
! 337: " TYPE=\"text/css\" media=\"all\">\n"
! 338: "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
! 339: " TYPE=\"text/css\" media=\"all\">\n"
! 340: "<TITLE>System Manpage Reference</TITLE>\n"
! 341: "</HEAD>\n"
! 342: "<BODY>\n"
! 343: "<!-- Begin page content. //-->\n",
! 344: cssdir, cssdir);
! 345: }
! 346:
! 347: static void
! 348: resp_end_html(void)
! 349: {
! 350:
! 351: puts("</BODY>\n"
! 352: "</HTML>");
! 353: }
! 354:
! 355: static void
! 356: resp_searchform(const struct req *req)
! 357: {
! 358: int i;
! 359:
! 360: puts("<!-- Begin search form. //-->");
! 361: printf("<DIV ID=\"mancgi\">\n"
! 362: "<FORM ACTION=\"%s/search\" METHOD=\"get\">\n"
! 363: "<FIELDSET>\n"
! 364: "<LEGEND>Search Parameters</LEGEND>\n"
! 365: "<INPUT TYPE=\"submit\" "
! 366: " VALUE=\"Search\"> for manuals matching \n"
! 367: "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"",
! 368: scriptname);
! 369: html_print(req->q.expr ? req->q.expr : "");
! 370: printf("\">, section "
! 371: "<INPUT TYPE=\"text\""
! 372: " SIZE=\"4\" NAME=\"sec\" VALUE=\"");
! 373: html_print(req->q.sec ? req->q.sec : "");
! 374: printf("\">, arch "
! 375: "<INPUT TYPE=\"text\""
! 376: " SIZE=\"8\" NAME=\"arch\" VALUE=\"");
! 377: html_print(req->q.arch ? req->q.arch : "");
! 378: printf("\">");
! 379: if (req->psz > 1) {
! 380: puts(", in <SELECT NAME=\"manpath\">");
! 381: for (i = 0; i < (int)req->psz; i++) {
! 382: printf("<OPTION ");
! 383: if (NULL == req->q.manpath ? 0 == i :
! 384: 0 == strcmp(req->q.manpath, req->p[i]))
! 385: printf("SELECTED=\"selected\" ");
! 386: printf("VALUE=\"");
! 387: html_print(req->p[i]);
! 388: printf("\">");
! 389: html_print(req->p[i]);
! 390: puts("</OPTION>");
! 391: }
! 392: puts("</SELECT>");
! 393: }
! 394: puts("—\n"
! 395: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
! 396: "</FIELDSET>\n"
! 397: "</FORM>\n"
! 398: "</DIV>");
! 399: puts("<!-- End search form. //-->");
! 400: }
! 401:
! 402: static void
! 403: resp_index(const struct req *req)
! 404: {
! 405:
! 406: resp_begin_html(200, NULL);
! 407: puts("<H1>\n"
! 408: "Online manuals with "
! 409: "<A HREF=\"http://mdocml.bsd.lv/\">mandoc</A>\n"
! 410: "</H1>");
! 411: resp_searchform(req);
! 412: puts("<P>\n"
! 413: "This web interface is documented in the "
! 414: "<A HREF=\"search?expr=Nm~^man\\.cgi$&sec=8\">"
! 415: "man.cgi</A> manual, and the "
! 416: "<A HREF=\"search?expr=Nm~^apropos$&sec=1\">"
! 417: "apropos</A> manual explains the query syntax.\n"
! 418: "</P>");
! 419: resp_end_html();
! 420: }
! 421:
! 422: static void
! 423: resp_noresult(const struct req *req, const char *msg)
! 424: {
! 425: resp_begin_html(200, NULL);
! 426: resp_searchform(req);
! 427: puts("<P>");
! 428: puts(msg);
! 429: puts("</P>");
! 430: resp_end_html();
! 431: }
! 432:
! 433: static void
! 434: resp_error_badrequest(const char *msg)
! 435: {
! 436:
! 437: resp_begin_html(400, "Bad Request");
! 438: puts("<H1>Bad Request</H1>\n"
! 439: "<P>\n");
! 440: puts(msg);
! 441: printf("Try again from the\n"
! 442: "<A HREF=\"%s\">main page</A>.\n"
! 443: "</P>", scriptname);
! 444: resp_end_html();
! 445: }
! 446:
! 447: static void
! 448: resp_error_notfound(const char *page)
! 449: {
! 450:
! 451: resp_begin_html(404, "Not Found");
! 452: puts("<H1>Page Not Found</H1>\n"
! 453: "<P>\n"
! 454: "The page you're looking for, ");
! 455: printf("<B>");
! 456: html_print(page);
! 457: printf("</B>,\n"
! 458: "could not be found.\n"
! 459: "Try searching from the\n"
! 460: "<A HREF=\"%s\">main page</A>.\n"
! 461: "</P>", scriptname);
! 462: resp_end_html();
! 463: }
! 464:
! 465: static void
! 466: resp_error_internal(void)
! 467: {
! 468: resp_begin_html(500, "Internal Server Error");
! 469: puts("<P>Internal Server Error</P>");
! 470: resp_end_html();
! 471: }
! 472:
! 473: static void
! 474: resp_search(const struct req *req, struct manpage *r, size_t sz)
! 475: {
! 476: size_t i;
! 477:
! 478: if (1 == sz) {
! 479: /*
! 480: * If we have just one result, then jump there now
! 481: * without any delay.
! 482: */
! 483: puts("Status: 303 See Other");
! 484: printf("Location: http://%s%s/show/%s/%s?",
! 485: httphost, scriptname, req->q.manpath, r[0].file);
! 486: http_printquery(req);
! 487: puts("\n"
! 488: "Content-Type: text/html; charset=utf-8\n");
! 489: return;
! 490: }
! 491:
! 492: qsort(r, sz, sizeof(struct manpage), cmp);
! 493:
! 494: resp_begin_html(200, NULL);
! 495: resp_searchform(req);
! 496: puts("<DIV CLASS=\"results\">");
! 497: puts("<TABLE>");
! 498:
! 499: for (i = 0; i < sz; i++) {
! 500: printf("<TR>\n"
! 501: "<TD CLASS=\"title\">\n"
! 502: "<A HREF=\"%s/show/%s/%s?",
! 503: scriptname, req->q.manpath, r[i].file);
! 504: html_printquery(req);
! 505: printf("\">");
! 506: html_print(r[i].names);
! 507: printf("</A>\n"
! 508: "</TD>\n"
! 509: "<TD CLASS=\"desc\">");
! 510: html_print(r[i].output);
! 511: puts("</TD>\n"
! 512: "</TR>");
! 513: }
! 514:
! 515: puts("</TABLE>\n"
! 516: "</DIV>");
! 517: resp_end_html();
! 518: }
! 519:
! 520: /* ARGSUSED */
! 521: static void
! 522: pg_index(const struct req *req, char *path)
! 523: {
! 524:
! 525: resp_index(req);
! 526: }
! 527:
! 528: static void
! 529: catman(const struct req *req, const char *file)
! 530: {
! 531: FILE *f;
! 532: size_t len;
! 533: int i;
! 534: char *p;
! 535: int italic, bold;
! 536:
! 537: if (NULL == (f = fopen(file, "r"))) {
! 538: resp_error_badrequest(
! 539: "You specified an invalid manual file.");
! 540: return;
! 541: }
! 542:
! 543: resp_begin_html(200, NULL);
! 544: resp_searchform(req);
! 545: puts("<DIV CLASS=\"catman\">\n"
! 546: "<PRE>");
! 547:
! 548: while (NULL != (p = fgetln(f, &len))) {
! 549: bold = italic = 0;
! 550: for (i = 0; i < (int)len - 1; i++) {
! 551: /*
! 552: * This means that the catpage is out of state.
! 553: * Ignore it and keep going (although the
! 554: * catpage is bogus).
! 555: */
! 556:
! 557: if ('\b' == p[i] || '\n' == p[i])
! 558: continue;
! 559:
! 560: /*
! 561: * Print a regular character.
! 562: * Close out any bold/italic scopes.
! 563: * If we're in back-space mode, make sure we'll
! 564: * have something to enter when we backspace.
! 565: */
! 566:
! 567: if ('\b' != p[i + 1]) {
! 568: if (italic)
! 569: printf("</I>");
! 570: if (bold)
! 571: printf("</B>");
! 572: italic = bold = 0;
! 573: html_putchar(p[i]);
! 574: continue;
! 575: } else if (i + 2 >= (int)len)
! 576: continue;
! 577:
! 578: /* Italic mode. */
! 579:
! 580: if ('_' == p[i]) {
! 581: if (bold)
! 582: printf("</B>");
! 583: if ( ! italic)
! 584: printf("<I>");
! 585: bold = 0;
! 586: italic = 1;
! 587: i += 2;
! 588: html_putchar(p[i]);
! 589: continue;
! 590: }
! 591:
! 592: /*
! 593: * Handle funny behaviour troff-isms.
! 594: * These grok'd from the original man2html.c.
! 595: */
! 596:
! 597: if (('+' == p[i] && 'o' == p[i + 2]) ||
! 598: ('o' == p[i] && '+' == p[i + 2]) ||
! 599: ('|' == p[i] && '=' == p[i + 2]) ||
! 600: ('=' == p[i] && '|' == p[i + 2]) ||
! 601: ('*' == p[i] && '=' == p[i + 2]) ||
! 602: ('=' == p[i] && '*' == p[i + 2]) ||
! 603: ('*' == p[i] && '|' == p[i + 2]) ||
! 604: ('|' == p[i] && '*' == p[i + 2])) {
! 605: if (italic)
! 606: printf("</I>");
! 607: if (bold)
! 608: printf("</B>");
! 609: italic = bold = 0;
! 610: putchar('*');
! 611: i += 2;
! 612: continue;
! 613: } else if (('|' == p[i] && '-' == p[i + 2]) ||
! 614: ('-' == p[i] && '|' == p[i + 1]) ||
! 615: ('+' == p[i] && '-' == p[i + 1]) ||
! 616: ('-' == p[i] && '+' == p[i + 1]) ||
! 617: ('+' == p[i] && '|' == p[i + 1]) ||
! 618: ('|' == p[i] && '+' == p[i + 1])) {
! 619: if (italic)
! 620: printf("</I>");
! 621: if (bold)
! 622: printf("</B>");
! 623: italic = bold = 0;
! 624: putchar('+');
! 625: i += 2;
! 626: continue;
! 627: }
! 628:
! 629: /* Bold mode. */
! 630:
! 631: if (italic)
! 632: printf("</I>");
! 633: if ( ! bold)
! 634: printf("<B>");
! 635: bold = 1;
! 636: italic = 0;
! 637: i += 2;
! 638: html_putchar(p[i]);
! 639: }
! 640:
! 641: /*
! 642: * Clean up the last character.
! 643: * We can get to a newline; don't print that.
! 644: */
! 645:
! 646: if (italic)
! 647: printf("</I>");
! 648: if (bold)
! 649: printf("</B>");
! 650:
! 651: if (i == (int)len - 1 && '\n' != p[i])
! 652: html_putchar(p[i]);
! 653:
! 654: putchar('\n');
! 655: }
! 656:
! 657: puts("</PRE>\n"
! 658: "</DIV>\n"
! 659: "</BODY>\n"
! 660: "</HTML>");
! 661:
! 662: fclose(f);
! 663: }
! 664:
! 665: static void
! 666: format(const struct req *req, const char *file)
! 667: {
! 668: struct mparse *mp;
! 669: int fd;
! 670: struct mdoc *mdoc;
! 671: struct man *man;
! 672: void *vp;
! 673: enum mandoclevel rc;
! 674: char opts[PATH_MAX + 128];
! 675:
! 676: if (-1 == (fd = open(file, O_RDONLY, 0))) {
! 677: resp_error_badrequest(
! 678: "You specified an invalid manual file.");
! 679: return;
! 680: }
! 681:
! 682: mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL,
! 683: req->q.manpath);
! 684: rc = mparse_readfd(mp, fd, file);
! 685: close(fd);
! 686:
! 687: if (rc >= MANDOCLEVEL_FATAL) {
! 688: fprintf(stderr, "fatal mandoc error: %s/%s\n",
! 689: req->q.manpath, file);
! 690: resp_error_internal();
! 691: return;
! 692: }
! 693:
! 694: snprintf(opts, sizeof(opts),
! 695: "fragment,man=%s/search?sec=%%S&expr=Nm~^%%N$",
! 696: scriptname);
! 697:
! 698: mparse_result(mp, &mdoc, &man, NULL);
! 699: if (NULL == man && NULL == mdoc) {
! 700: fprintf(stderr, "fatal mandoc error: %s/%s\n",
! 701: req->q.manpath, file);
! 702: resp_error_internal();
! 703: mparse_free(mp);
! 704: return;
! 705: }
! 706:
! 707: resp_begin_html(200, NULL);
! 708: resp_searchform(req);
! 709:
! 710: vp = html_alloc(opts);
! 711:
! 712: if (NULL != mdoc)
! 713: html_mdoc(vp, mdoc);
! 714: else
! 715: html_man(vp, man);
! 716:
! 717: puts("</BODY>\n"
! 718: "</HTML>");
! 719:
! 720: html_free(vp);
! 721: mparse_free(mp);
! 722: }
! 723:
! 724: static void
! 725: pg_show(const struct req *req, char *path)
! 726: {
! 727: char *sub;
! 728:
! 729: if (NULL == path || NULL == (sub = strchr(path, '/'))) {
! 730: resp_error_badrequest(
! 731: "You did not specify a page to show.");
! 732: return;
! 733: }
! 734: *sub++ = '\0';
! 735:
! 736: /*
! 737: * Begin by chdir()ing into the manpath.
! 738: * This way we can pick up the database files, which are
! 739: * relative to the manpath root.
! 740: */
! 741:
! 742: if (-1 == chdir(path)) {
! 743: resp_error_badrequest(
! 744: "You specified an invalid manpath.");
! 745: return;
! 746: }
! 747:
! 748: if ('c' == *sub)
! 749: catman(req, sub);
! 750: else
! 751: format(req, sub);
! 752: }
! 753:
! 754: static void
! 755: pg_search(const struct req *req, char *path)
! 756: {
! 757: struct mansearch search;
! 758: struct manpaths paths;
! 759: struct manpage *res;
! 760: char **cp;
! 761: const char *ep, *start;
! 762: size_t ressz;
! 763: int i, sz;
! 764:
! 765: /*
! 766: * Begin by chdir()ing into the root of the manpath.
! 767: * This way we can pick up the database files, which are
! 768: * relative to the manpath root.
! 769: */
! 770:
! 771: if (-1 == (chdir(req->q.manpath))) {
! 772: resp_error_badrequest(
! 773: "You specified an invalid manpath.");
! 774: return;
! 775: }
! 776:
! 777: search.arch = req->q.arch;
! 778: search.sec = req->q.sec;
! 779: search.deftype = TYPE_Nm | TYPE_Nd;
! 780: search.flags = 0;
! 781:
! 782: paths.sz = 1;
! 783: paths.paths = mandoc_malloc(sizeof(char *));
! 784: paths.paths[0] = mandoc_strdup(".");
! 785:
! 786: /*
! 787: * Poor man's tokenisation: just break apart by spaces.
! 788: * Yes, this is half-ass. But it works for now.
! 789: */
! 790:
! 791: ep = req->q.expr;
! 792: while (ep && isspace((unsigned char)*ep))
! 793: ep++;
! 794:
! 795: sz = 0;
! 796: cp = NULL;
! 797: while (ep && '\0' != *ep) {
! 798: cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
! 799: start = ep;
! 800: while ('\0' != *ep && ! isspace((unsigned char)*ep))
! 801: ep++;
! 802: cp[sz] = mandoc_malloc((ep - start) + 1);
! 803: memcpy(cp[sz], start, ep - start);
! 804: cp[sz++][ep - start] = '\0';
! 805: while (isspace((unsigned char)*ep))
! 806: ep++;
! 807: }
! 808:
! 809: if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
! 810: resp_noresult(req, "You entered an invalid query.");
! 811: else if (0 == ressz)
! 812: resp_noresult(req, "No results found.");
! 813: else
! 814: resp_search(req, res, ressz);
! 815:
! 816: for (i = 0; i < sz; i++)
! 817: free(cp[i]);
! 818: free(cp);
! 819:
! 820: for (i = 0; i < (int)ressz; i++) {
! 821: free(res[i].file);
! 822: free(res[i].names);
! 823: free(res[i].output);
! 824: }
! 825: free(res);
! 826:
! 827: free(paths.paths[0]);
! 828: free(paths.paths);
! 829: }
! 830:
! 831: int
! 832: main(void)
! 833: {
! 834: int i;
! 835: struct req req;
! 836: char *querystring, *path, *subpath;
! 837:
! 838: /* Scan our run-time environment. */
! 839:
! 840: if (NULL == (mandir = getenv("MAN_DIR")))
! 841: mandir = "/man";
! 842:
! 843: if (NULL == (scriptname = getenv("SCRIPT_NAME")))
! 844: scriptname = "";
! 845:
! 846: if (NULL == (cssdir = getenv("CSS_DIR")))
! 847: cssdir = "";
! 848:
! 849: if (NULL == (httphost = getenv("HTTP_HOST")))
! 850: httphost = "localhost";
! 851:
! 852: /*
! 853: * First we change directory into the mandir so that
! 854: * subsequent scanning for manpath directories is rooted
! 855: * relative to the same position.
! 856: */
! 857:
! 858: if (-1 == chdir(mandir)) {
! 859: fprintf(stderr, "MAN_DIR: %s: %s\n",
! 860: mandir, strerror(errno));
! 861: resp_error_internal();
! 862: return(EXIT_FAILURE);
! 863: }
! 864:
! 865: memset(&req, 0, sizeof(struct req));
! 866: pathgen(&req);
! 867:
! 868: /* Next parse out the query string. */
! 869:
! 870: if (NULL != (querystring = getenv("QUERY_STRING")))
! 871: http_parse(&req, querystring);
! 872:
! 873: /*
! 874: * Now juggle paths to extract information.
! 875: * We want to extract our filetype (the file suffix), the
! 876: * initial path component, then the trailing component(s).
! 877: * Start with leading subpath component.
! 878: */
! 879:
! 880: subpath = path = NULL;
! 881: req.page = PAGE__MAX;
! 882:
! 883: if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
! 884: req.page = PAGE_INDEX;
! 885:
! 886: if (NULL != path && '/' == *path && '\0' == *++path)
! 887: req.page = PAGE_INDEX;
! 888:
! 889: /* Resolve subpath component. */
! 890:
! 891: if (NULL != path && NULL != (subpath = strchr(path, '/')))
! 892: *subpath++ = '\0';
! 893:
! 894: /* Map path into one we recognise. */
! 895:
! 896: if (NULL != path && '\0' != *path)
! 897: for (i = 0; i < (int)PAGE__MAX; i++)
! 898: if (0 == strcmp(pages[i], path)) {
! 899: req.page = (enum page)i;
! 900: break;
! 901: }
! 902:
! 903: /* Route pages. */
! 904:
! 905: switch (req.page) {
! 906: case (PAGE_INDEX):
! 907: pg_index(&req, subpath);
! 908: break;
! 909: case (PAGE_SEARCH):
! 910: pg_search(&req, subpath);
! 911: break;
! 912: case (PAGE_SHOW):
! 913: pg_show(&req, subpath);
! 914: break;
! 915: default:
! 916: resp_error_notfound(path);
! 917: break;
! 918: }
! 919:
! 920: for (i = 0; i < (int)req.psz; i++)
! 921: free(req.p[i]);
! 922: free(req.p);
! 923: return(EXIT_SUCCESS);
! 924: }
! 925:
! 926: static int
! 927: cmp(const void *p1, const void *p2)
! 928: {
! 929:
! 930: return(strcasecmp(((const struct manpage *)p1)->names,
! 931: ((const struct manpage *)p2)->names));
! 932: }
! 933:
! 934: /*
! 935: * Scan for indexable paths.
! 936: */
! 937: static void
! 938: pathgen(struct req *req)
! 939: {
! 940: FILE *fp;
! 941: char *dp;
! 942: size_t dpsz;
! 943:
! 944: if (NULL == (fp = fopen("manpath.conf", "r")))
! 945: return;
! 946:
! 947: while (NULL != (dp = fgetln(fp, &dpsz))) {
! 948: if ('\n' == dp[dpsz - 1])
! 949: dpsz--;
! 950: req->p = mandoc_realloc(req->p,
! 951: (req->psz + 1) * sizeof(char *));
! 952: req->p[req->psz++] = mandoc_strndup(dp, dpsz);
! 953: }
! 954: }