Annotation of src/usr.bin/mandoc/cgi.c, Revision 1.5
1.5 ! schwarze 1: /* $Id: cgi.c,v 1.4 2014/07/12 01:52:57 schwarze Exp $ */
1.1 schwarze 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 */
1.5 ! schwarze 48: int equal; /* match whole names, not substrings */
1.1 schwarze 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) {
1.5 ! schwarze 141: printf("&query=");
! 142: http_print(req->q.expr);
1.1 schwarze 143: }
1.5 ! schwarze 144: if (0 == req->q.equal)
! 145: printf("&apropos=1");
1.1 schwarze 146: }
147:
148: static void
149: html_printquery(const struct req *req)
150: {
151:
152: if (NULL != req->q.manpath) {
153: printf("&manpath=");
154: html_print(req->q.manpath);
155: }
156: if (NULL != req->q.sec) {
157: printf("&sec=");
158: html_print(req->q.sec);
159: }
160: if (NULL != req->q.arch) {
161: printf("&arch=");
162: html_print(req->q.arch);
163: }
164: if (NULL != req->q.expr) {
1.5 ! schwarze 165: printf("&query=");
1.3 tedu 166: html_print(req->q.expr);
1.1 schwarze 167: }
1.5 ! schwarze 168: if (0 == req->q.equal)
! 169: printf("&apropos=1");
1.1 schwarze 170: }
171:
172: static void
173: http_print(const char *p)
174: {
175:
176: if (NULL == p)
177: return;
178: while ('\0' != *p)
179: http_putchar(*p++);
180: }
181:
182: /*
183: * Call through to html_putchar().
184: * Accepts NULL strings.
185: */
186: static void
187: html_print(const char *p)
188: {
189:
190: if (NULL == p)
191: return;
192: while ('\0' != *p)
193: html_putchar(*p++);
194: }
195:
196: /*
197: * Parse out key-value pairs from an HTTP request variable.
198: * This can be either a cookie or a POST/GET string, although man.cgi
199: * uses only GET for simplicity.
200: */
201: static void
202: http_parse(struct req *req, char *p)
203: {
204: char *key, *val;
205:
206: memset(&req->q, 0, sizeof(struct query));
207: req->q.manpath = req->p[0];
1.5 ! schwarze 208: req->q.equal = 1;
1.1 schwarze 209:
210: while ('\0' != *p) {
211: key = p;
212: val = NULL;
213:
214: p += (int)strcspn(p, ";&");
215: if ('\0' != *p)
216: *p++ = '\0';
217: if (NULL != (val = strchr(key, '=')))
218: *val++ = '\0';
219:
220: if ('\0' == *key || NULL == val || '\0' == *val)
221: continue;
222:
223: /* Just abort handling. */
224:
225: if ( ! http_decode(key))
226: break;
227: if (NULL != val && ! http_decode(val))
228: break;
229:
1.5 ! schwarze 230: if (0 == strcmp(key, "query"))
1.1 schwarze 231: req->q.expr = val;
232: else if (0 == strcmp(key, "manpath"))
233: req->q.manpath = val;
234: else if (0 == strcmp(key, "apropos"))
1.5 ! schwarze 235: req->q.equal = !strcmp(val, "0");
! 236: else if (0 == strcmp(key, "sec") ||
! 237: 0 == strcmp(key, "sektion")) {
! 238: if (strcmp(val, "0"))
! 239: req->q.sec = val;
! 240: } else if (0 == strcmp(key, "arch")) {
! 241: if (strcmp(val, "default"))
! 242: req->q.arch = val;
! 243: }
1.1 schwarze 244: }
245: }
246:
247: static void
248: http_putchar(char c)
249: {
250:
251: if (isalnum((unsigned char)c)) {
252: putchar((unsigned char)c);
253: return;
254: } else if (' ' == c) {
255: putchar('+');
256: return;
257: }
258: printf("%%%.2x", c);
259: }
260:
261: /*
262: * HTTP-decode a string. The standard explanation is that this turns
263: * "%4e+foo" into "n foo" in the regular way. This is done in-place
264: * over the allocated string.
265: */
266: static int
267: http_decode(char *p)
268: {
269: char hex[3];
1.3 tedu 270: char *q;
1.1 schwarze 271: int c;
272:
273: hex[2] = '\0';
274:
1.3 tedu 275: q = p;
276: for ( ; '\0' != *p; p++, q++) {
1.1 schwarze 277: if ('%' == *p) {
278: if ('\0' == (hex[0] = *(p + 1)))
279: return(0);
280: if ('\0' == (hex[1] = *(p + 2)))
281: return(0);
282: if (1 != sscanf(hex, "%x", &c))
283: return(0);
284: if ('\0' == c)
285: return(0);
286:
1.3 tedu 287: *q = (char)c;
288: p += 2;
1.1 schwarze 289: } else
1.3 tedu 290: *q = '+' == *p ? ' ' : *p;
1.1 schwarze 291: }
292:
1.3 tedu 293: *q = '\0';
1.1 schwarze 294: return(1);
295: }
296:
297: static void
298: resp_begin_http(int code, const char *msg)
299: {
300:
301: if (200 != code)
1.2 tedu 302: printf("Status: %d %s\r\n", code, msg);
1.1 schwarze 303:
1.2 tedu 304: printf("Content-Type: text/html; charset=utf-8\r\n"
305: "Cache-Control: no-cache\r\n"
306: "Pragma: no-cache\r\n"
307: "\r\n");
1.1 schwarze 308:
309: fflush(stdout);
310: }
311:
312: static void
313: resp_begin_html(int code, const char *msg)
314: {
315:
316: resp_begin_http(code, msg);
317:
318: printf("<!DOCTYPE HTML PUBLIC "
319: " \"-//W3C//DTD HTML 4.01//EN\""
320: " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
321: "<HTML>\n"
322: "<HEAD>\n"
323: "<META HTTP-EQUIV=\"Content-Type\""
324: " CONTENT=\"text/html; charset=utf-8\">\n"
325: "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
326: " TYPE=\"text/css\" media=\"all\">\n"
327: "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
328: " TYPE=\"text/css\" media=\"all\">\n"
329: "<TITLE>System Manpage Reference</TITLE>\n"
330: "</HEAD>\n"
331: "<BODY>\n"
332: "<!-- Begin page content. //-->\n",
333: cssdir, cssdir);
334: }
335:
336: static void
337: resp_end_html(void)
338: {
339:
340: puts("</BODY>\n"
341: "</HTML>");
342: }
343:
344: static void
345: resp_searchform(const struct req *req)
346: {
347: int i;
348:
349: puts("<!-- Begin search form. //-->");
350: printf("<DIV ID=\"mancgi\">\n"
351: "<FORM ACTION=\"%s/search\" METHOD=\"get\">\n"
352: "<FIELDSET>\n"
353: "<LEGEND>Search Parameters</LEGEND>\n"
1.5 ! schwarze 354: "<INPUT TYPE=\"submit\" VALUE=\"Search\"> "
! 355: "for manuals \n",
1.1 schwarze 356: scriptname);
1.5 ! schwarze 357: printf("<SELECT NAME=\"apropos\">\n"
! 358: "<OPTION VALUE=\"0\"");
! 359: if (req->q.equal)
! 360: printf(" SELECTED=\"selected\"");
! 361: printf(">named</OPTION>\n"
! 362: "<OPTION VALUE=\"1\"");
! 363: if (0 == req->q.equal)
! 364: printf(" SELECTED=\"selected\"");
! 365: printf(">matching</OPTION>\n"
! 366: "</SELECT>\n"
! 367: "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
1.1 schwarze 368: html_print(req->q.expr ? req->q.expr : "");
369: printf("\">, section "
370: "<INPUT TYPE=\"text\""
371: " SIZE=\"4\" NAME=\"sec\" VALUE=\"");
372: html_print(req->q.sec ? req->q.sec : "");
373: printf("\">, arch "
374: "<INPUT TYPE=\"text\""
375: " SIZE=\"8\" NAME=\"arch\" VALUE=\"");
376: html_print(req->q.arch ? req->q.arch : "");
377: printf("\">");
378: if (req->psz > 1) {
379: puts(", in <SELECT NAME=\"manpath\">");
380: for (i = 0; i < (int)req->psz; i++) {
381: printf("<OPTION ");
382: if (NULL == req->q.manpath ? 0 == i :
383: 0 == strcmp(req->q.manpath, req->p[i]))
384: printf("SELECTED=\"selected\" ");
385: printf("VALUE=\"");
386: html_print(req->p[i]);
387: printf("\">");
388: html_print(req->p[i]);
389: puts("</OPTION>");
390: }
391: puts("</SELECT>");
392: }
393: puts("—\n"
394: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
395: "</FIELDSET>\n"
396: "</FORM>\n"
397: "</DIV>");
398: puts("<!-- End search form. //-->");
399: }
400:
401: static void
402: resp_index(const struct req *req)
403: {
404:
405: resp_begin_html(200, NULL);
406: puts("<H1>\n"
407: "Online manuals with "
408: "<A HREF=\"http://mdocml.bsd.lv/\">mandoc</A>\n"
409: "</H1>");
410: resp_searchform(req);
1.4 schwarze 411: printf("<P>\n"
412: "This web interface is documented in the "
413: "<A HREF=\"%s/search?expr=Nm~^man\\.cgi$&sec=8\">"
414: "man.cgi</A> manual, and the "
415: "<A HREF=\"%s/search?expr=Nm~^apropos$&sec=1\">"
416: "apropos</A> manual explains the query syntax.\n"
417: "</P>\n",
418: scriptname, scriptname);
1.1 schwarze 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: */
1.2 tedu 483: printf("Status: 303 See Other\r\n");
1.1 schwarze 484: printf("Location: http://%s%s/show/%s/%s?",
485: httphost, scriptname, req->q.manpath, r[0].file);
486: http_printquery(req);
1.2 tedu 487: printf("\r\n"
488: "Content-Type: text/html; charset=utf-8\r\n"
489: "\r\n");
1.1 schwarze 490: return;
491: }
492:
493: qsort(r, sz, sizeof(struct manpage), cmp);
494:
495: resp_begin_html(200, NULL);
496: resp_searchform(req);
497: puts("<DIV CLASS=\"results\">");
498: puts("<TABLE>");
499:
500: for (i = 0; i < sz; i++) {
501: printf("<TR>\n"
502: "<TD CLASS=\"title\">\n"
503: "<A HREF=\"%s/show/%s/%s?",
504: scriptname, req->q.manpath, r[i].file);
505: html_printquery(req);
506: printf("\">");
507: html_print(r[i].names);
508: printf("</A>\n"
509: "</TD>\n"
510: "<TD CLASS=\"desc\">");
511: html_print(r[i].output);
512: puts("</TD>\n"
513: "</TR>");
514: }
515:
516: puts("</TABLE>\n"
517: "</DIV>");
518: resp_end_html();
519: }
520:
521: /* ARGSUSED */
522: static void
523: pg_index(const struct req *req, char *path)
524: {
525:
526: resp_index(req);
527: }
528:
529: static void
530: catman(const struct req *req, const char *file)
531: {
532: FILE *f;
533: size_t len;
534: int i;
535: char *p;
536: int italic, bold;
537:
538: if (NULL == (f = fopen(file, "r"))) {
539: resp_error_badrequest(
540: "You specified an invalid manual file.");
541: return;
542: }
543:
544: resp_begin_html(200, NULL);
545: resp_searchform(req);
546: puts("<DIV CLASS=\"catman\">\n"
547: "<PRE>");
548:
549: while (NULL != (p = fgetln(f, &len))) {
550: bold = italic = 0;
551: for (i = 0; i < (int)len - 1; i++) {
552: /*
553: * This means that the catpage is out of state.
554: * Ignore it and keep going (although the
555: * catpage is bogus).
556: */
557:
558: if ('\b' == p[i] || '\n' == p[i])
559: continue;
560:
561: /*
562: * Print a regular character.
563: * Close out any bold/italic scopes.
564: * If we're in back-space mode, make sure we'll
565: * have something to enter when we backspace.
566: */
567:
568: if ('\b' != p[i + 1]) {
569: if (italic)
570: printf("</I>");
571: if (bold)
572: printf("</B>");
573: italic = bold = 0;
574: html_putchar(p[i]);
575: continue;
576: } else if (i + 2 >= (int)len)
577: continue;
578:
579: /* Italic mode. */
580:
581: if ('_' == p[i]) {
582: if (bold)
583: printf("</B>");
584: if ( ! italic)
585: printf("<I>");
586: bold = 0;
587: italic = 1;
588: i += 2;
589: html_putchar(p[i]);
590: continue;
591: }
592:
593: /*
594: * Handle funny behaviour troff-isms.
595: * These grok'd from the original man2html.c.
596: */
597:
598: if (('+' == p[i] && 'o' == p[i + 2]) ||
599: ('o' == 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: ('|' == p[i] && '*' == p[i + 2])) {
606: if (italic)
607: printf("</I>");
608: if (bold)
609: printf("</B>");
610: italic = bold = 0;
611: putchar('*');
612: i += 2;
613: continue;
614: } else if (('|' == p[i] && '-' == p[i + 2]) ||
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: ('|' == p[i] && '+' == p[i + 1])) {
620: if (italic)
621: printf("</I>");
622: if (bold)
623: printf("</B>");
624: italic = bold = 0;
625: putchar('+');
626: i += 2;
627: continue;
628: }
629:
630: /* Bold mode. */
631:
632: if (italic)
633: printf("</I>");
634: if ( ! bold)
635: printf("<B>");
636: bold = 1;
637: italic = 0;
638: i += 2;
639: html_putchar(p[i]);
640: }
641:
642: /*
643: * Clean up the last character.
644: * We can get to a newline; don't print that.
645: */
646:
647: if (italic)
648: printf("</I>");
649: if (bold)
650: printf("</B>");
651:
652: if (i == (int)len - 1 && '\n' != p[i])
653: html_putchar(p[i]);
654:
655: putchar('\n');
656: }
657:
658: puts("</PRE>\n"
659: "</DIV>\n"
660: "</BODY>\n"
661: "</HTML>");
662:
663: fclose(f);
664: }
665:
666: static void
667: format(const struct req *req, const char *file)
668: {
669: struct mparse *mp;
670: int fd;
671: struct mdoc *mdoc;
672: struct man *man;
673: void *vp;
674: enum mandoclevel rc;
675: char opts[PATH_MAX + 128];
676:
677: if (-1 == (fd = open(file, O_RDONLY, 0))) {
678: resp_error_badrequest(
679: "You specified an invalid manual file.");
680: return;
681: }
682:
683: mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL,
684: req->q.manpath);
685: rc = mparse_readfd(mp, fd, file);
686: close(fd);
687:
688: if (rc >= MANDOCLEVEL_FATAL) {
689: fprintf(stderr, "fatal mandoc error: %s/%s\n",
690: req->q.manpath, file);
691: resp_error_internal();
692: return;
693: }
694:
695: snprintf(opts, sizeof(opts),
696: "fragment,man=%s/search?sec=%%S&expr=Nm~^%%N$",
697: scriptname);
698:
699: mparse_result(mp, &mdoc, &man, NULL);
700: if (NULL == man && NULL == mdoc) {
701: fprintf(stderr, "fatal mandoc error: %s/%s\n",
702: req->q.manpath, file);
703: resp_error_internal();
704: mparse_free(mp);
705: return;
706: }
707:
708: resp_begin_html(200, NULL);
709: resp_searchform(req);
710:
711: vp = html_alloc(opts);
712:
713: if (NULL != mdoc)
714: html_mdoc(vp, mdoc);
715: else
716: html_man(vp, man);
717:
718: puts("</BODY>\n"
719: "</HTML>");
720:
721: html_free(vp);
722: mparse_free(mp);
723: }
724:
725: static void
726: pg_show(const struct req *req, char *path)
727: {
728: char *sub;
729:
730: if (NULL == path || NULL == (sub = strchr(path, '/'))) {
731: resp_error_badrequest(
732: "You did not specify a page to show.");
733: return;
734: }
735: *sub++ = '\0';
736:
737: /*
738: * Begin by chdir()ing into the manpath.
739: * This way we can pick up the database files, which are
740: * relative to the manpath root.
741: */
742:
743: if (-1 == chdir(path)) {
744: resp_error_badrequest(
745: "You specified an invalid manpath.");
746: return;
747: }
748:
749: if ('c' == *sub)
750: catman(req, sub);
751: else
752: format(req, sub);
753: }
754:
755: static void
756: pg_search(const struct req *req, char *path)
757: {
758: struct mansearch search;
759: struct manpaths paths;
760: struct manpage *res;
761: char **cp;
762: const char *ep, *start;
763: size_t ressz;
764: int i, sz;
765:
766: /*
767: * Begin by chdir()ing into the root of the manpath.
768: * This way we can pick up the database files, which are
769: * relative to the manpath root.
770: */
771:
772: if (-1 == (chdir(req->q.manpath))) {
773: resp_error_badrequest(
774: "You specified an invalid manpath.");
775: return;
776: }
777:
778: search.arch = req->q.arch;
779: search.sec = req->q.sec;
1.5 ! schwarze 780: search.deftype = req->q.equal ? TYPE_Nm : (TYPE_Nm | TYPE_Nd);
! 781: search.flags = req->q.equal ? MANSEARCH_MAN : 0;
1.1 schwarze 782:
783: paths.sz = 1;
784: paths.paths = mandoc_malloc(sizeof(char *));
785: paths.paths[0] = mandoc_strdup(".");
786:
787: /*
788: * Poor man's tokenisation: just break apart by spaces.
789: * Yes, this is half-ass. But it works for now.
790: */
791:
792: ep = req->q.expr;
793: while (ep && isspace((unsigned char)*ep))
794: ep++;
795:
796: sz = 0;
797: cp = NULL;
798: while (ep && '\0' != *ep) {
799: cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
800: start = ep;
801: while ('\0' != *ep && ! isspace((unsigned char)*ep))
802: ep++;
803: cp[sz] = mandoc_malloc((ep - start) + 1);
804: memcpy(cp[sz], start, ep - start);
805: cp[sz++][ep - start] = '\0';
806: while (isspace((unsigned char)*ep))
807: ep++;
808: }
809:
810: if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
811: resp_noresult(req, "You entered an invalid query.");
812: else if (0 == ressz)
813: resp_noresult(req, "No results found.");
814: else
815: resp_search(req, res, ressz);
816:
817: for (i = 0; i < sz; i++)
818: free(cp[i]);
819: free(cp);
820:
821: for (i = 0; i < (int)ressz; i++) {
822: free(res[i].file);
823: free(res[i].names);
824: free(res[i].output);
825: }
826: free(res);
827:
828: free(paths.paths[0]);
829: free(paths.paths);
830: }
831:
832: int
833: main(void)
834: {
835: int i;
836: struct req req;
837: char *querystring, *path, *subpath;
838:
839: /* Scan our run-time environment. */
840:
841: if (NULL == (mandir = getenv("MAN_DIR")))
842: mandir = "/man";
843:
844: if (NULL == (scriptname = getenv("SCRIPT_NAME")))
845: scriptname = "";
846:
847: if (NULL == (cssdir = getenv("CSS_DIR")))
848: cssdir = "";
849:
850: if (NULL == (httphost = getenv("HTTP_HOST")))
851: httphost = "localhost";
852:
853: /*
854: * First we change directory into the mandir so that
855: * subsequent scanning for manpath directories is rooted
856: * relative to the same position.
857: */
858:
859: if (-1 == chdir(mandir)) {
860: fprintf(stderr, "MAN_DIR: %s: %s\n",
861: mandir, strerror(errno));
862: resp_error_internal();
863: return(EXIT_FAILURE);
864: }
865:
866: memset(&req, 0, sizeof(struct req));
867: pathgen(&req);
868:
869: /* Next parse out the query string. */
870:
871: if (NULL != (querystring = getenv("QUERY_STRING")))
872: http_parse(&req, querystring);
873:
874: /*
875: * Now juggle paths to extract information.
876: * We want to extract our filetype (the file suffix), the
877: * initial path component, then the trailing component(s).
878: * Start with leading subpath component.
879: */
880:
881: subpath = path = NULL;
882: req.page = PAGE__MAX;
883:
884: if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
885: req.page = PAGE_INDEX;
886:
887: if (NULL != path && '/' == *path && '\0' == *++path)
888: req.page = PAGE_INDEX;
889:
890: /* Resolve subpath component. */
891:
892: if (NULL != path && NULL != (subpath = strchr(path, '/')))
893: *subpath++ = '\0';
894:
895: /* Map path into one we recognise. */
896:
897: if (NULL != path && '\0' != *path)
898: for (i = 0; i < (int)PAGE__MAX; i++)
899: if (0 == strcmp(pages[i], path)) {
900: req.page = (enum page)i;
901: break;
902: }
903:
904: /* Route pages. */
905:
906: switch (req.page) {
907: case (PAGE_INDEX):
908: pg_index(&req, subpath);
909: break;
910: case (PAGE_SEARCH):
911: pg_search(&req, subpath);
912: break;
913: case (PAGE_SHOW):
914: pg_show(&req, subpath);
915: break;
916: default:
917: resp_error_notfound(path);
918: break;
919: }
920:
921: for (i = 0; i < (int)req.psz; i++)
922: free(req.p[i]);
923: free(req.p);
924: return(EXIT_SUCCESS);
925: }
926:
927: static int
928: cmp(const void *p1, const void *p2)
929: {
930:
931: return(strcasecmp(((const struct manpage *)p1)->names,
932: ((const struct manpage *)p2)->names));
933: }
934:
935: /*
936: * Scan for indexable paths.
937: */
938: static void
939: pathgen(struct req *req)
940: {
941: FILE *fp;
942: char *dp;
943: size_t dpsz;
944:
945: if (NULL == (fp = fopen("manpath.conf", "r")))
946: return;
947:
948: while (NULL != (dp = fgetln(fp, &dpsz))) {
949: if ('\n' == dp[dpsz - 1])
950: dpsz--;
951: req->p = mandoc_realloc(req->p,
952: (req->psz + 1) * sizeof(char *));
953: req->p[req->psz++] = mandoc_strndup(dp, dpsz);
954: }
955: }