Annotation of src/usr.bin/mandoc/cgi.c, Revision 1.4
1.4 ! schwarze 1: /* $Id: cgi.c,v 1.3 2014/07/11 22:16:11 tedu 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 */
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=");
1.3 tedu 164: html_print(req->q.expr);
1.1 schwarze 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];
1.3 tedu 283: char *q;
1.1 schwarze 284: int c;
285:
286: hex[2] = '\0';
287:
1.3 tedu 288: q = p;
289: for ( ; '\0' != *p; p++, q++) {
1.1 schwarze 290: if ('%' == *p) {
291: if ('\0' == (hex[0] = *(p + 1)))
292: return(0);
293: if ('\0' == (hex[1] = *(p + 2)))
294: return(0);
295: if (1 != sscanf(hex, "%x", &c))
296: return(0);
297: if ('\0' == c)
298: return(0);
299:
1.3 tedu 300: *q = (char)c;
301: p += 2;
1.1 schwarze 302: } else
1.3 tedu 303: *q = '+' == *p ? ' ' : *p;
1.1 schwarze 304: }
305:
1.3 tedu 306: *q = '\0';
1.1 schwarze 307: return(1);
308: }
309:
310: static void
311: resp_begin_http(int code, const char *msg)
312: {
313:
314: if (200 != code)
1.2 tedu 315: printf("Status: %d %s\r\n", code, msg);
1.1 schwarze 316:
1.2 tedu 317: printf("Content-Type: text/html; charset=utf-8\r\n"
318: "Cache-Control: no-cache\r\n"
319: "Pragma: no-cache\r\n"
320: "\r\n");
1.1 schwarze 321:
322: fflush(stdout);
323: }
324:
325: static void
326: resp_begin_html(int code, const char *msg)
327: {
328:
329: resp_begin_http(code, msg);
330:
331: printf("<!DOCTYPE HTML PUBLIC "
332: " \"-//W3C//DTD HTML 4.01//EN\""
333: " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
334: "<HTML>\n"
335: "<HEAD>\n"
336: "<META HTTP-EQUIV=\"Content-Type\""
337: " CONTENT=\"text/html; charset=utf-8\">\n"
338: "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
339: " TYPE=\"text/css\" media=\"all\">\n"
340: "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
341: " TYPE=\"text/css\" media=\"all\">\n"
342: "<TITLE>System Manpage Reference</TITLE>\n"
343: "</HEAD>\n"
344: "<BODY>\n"
345: "<!-- Begin page content. //-->\n",
346: cssdir, cssdir);
347: }
348:
349: static void
350: resp_end_html(void)
351: {
352:
353: puts("</BODY>\n"
354: "</HTML>");
355: }
356:
357: static void
358: resp_searchform(const struct req *req)
359: {
360: int i;
361:
362: puts("<!-- Begin search form. //-->");
363: printf("<DIV ID=\"mancgi\">\n"
364: "<FORM ACTION=\"%s/search\" METHOD=\"get\">\n"
365: "<FIELDSET>\n"
366: "<LEGEND>Search Parameters</LEGEND>\n"
367: "<INPUT TYPE=\"submit\" "
368: " VALUE=\"Search\"> for manuals matching \n"
369: "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"",
370: scriptname);
371: html_print(req->q.expr ? req->q.expr : "");
372: printf("\">, section "
373: "<INPUT TYPE=\"text\""
374: " SIZE=\"4\" NAME=\"sec\" VALUE=\"");
375: html_print(req->q.sec ? req->q.sec : "");
376: printf("\">, arch "
377: "<INPUT TYPE=\"text\""
378: " SIZE=\"8\" NAME=\"arch\" VALUE=\"");
379: html_print(req->q.arch ? req->q.arch : "");
380: printf("\">");
381: if (req->psz > 1) {
382: puts(", in <SELECT NAME=\"manpath\">");
383: for (i = 0; i < (int)req->psz; i++) {
384: printf("<OPTION ");
385: if (NULL == req->q.manpath ? 0 == i :
386: 0 == strcmp(req->q.manpath, req->p[i]))
387: printf("SELECTED=\"selected\" ");
388: printf("VALUE=\"");
389: html_print(req->p[i]);
390: printf("\">");
391: html_print(req->p[i]);
392: puts("</OPTION>");
393: }
394: puts("</SELECT>");
395: }
396: puts("—\n"
397: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
398: "</FIELDSET>\n"
399: "</FORM>\n"
400: "</DIV>");
401: puts("<!-- End search form. //-->");
402: }
403:
404: static void
405: resp_index(const struct req *req)
406: {
407:
408: resp_begin_html(200, NULL);
409: puts("<H1>\n"
410: "Online manuals with "
411: "<A HREF=\"http://mdocml.bsd.lv/\">mandoc</A>\n"
412: "</H1>");
413: resp_searchform(req);
1.4 ! schwarze 414: printf("<P>\n"
! 415: "This web interface is documented in the "
! 416: "<A HREF=\"%s/search?expr=Nm~^man\\.cgi$&sec=8\">"
! 417: "man.cgi</A> manual, and the "
! 418: "<A HREF=\"%s/search?expr=Nm~^apropos$&sec=1\">"
! 419: "apropos</A> manual explains the query syntax.\n"
! 420: "</P>\n",
! 421: scriptname, scriptname);
1.1 schwarze 422: resp_end_html();
423: }
424:
425: static void
426: resp_noresult(const struct req *req, const char *msg)
427: {
428: resp_begin_html(200, NULL);
429: resp_searchform(req);
430: puts("<P>");
431: puts(msg);
432: puts("</P>");
433: resp_end_html();
434: }
435:
436: static void
437: resp_error_badrequest(const char *msg)
438: {
439:
440: resp_begin_html(400, "Bad Request");
441: puts("<H1>Bad Request</H1>\n"
442: "<P>\n");
443: puts(msg);
444: printf("Try again from the\n"
445: "<A HREF=\"%s\">main page</A>.\n"
446: "</P>", scriptname);
447: resp_end_html();
448: }
449:
450: static void
451: resp_error_notfound(const char *page)
452: {
453:
454: resp_begin_html(404, "Not Found");
455: puts("<H1>Page Not Found</H1>\n"
456: "<P>\n"
457: "The page you're looking for, ");
458: printf("<B>");
459: html_print(page);
460: printf("</B>,\n"
461: "could not be found.\n"
462: "Try searching from the\n"
463: "<A HREF=\"%s\">main page</A>.\n"
464: "</P>", scriptname);
465: resp_end_html();
466: }
467:
468: static void
469: resp_error_internal(void)
470: {
471: resp_begin_html(500, "Internal Server Error");
472: puts("<P>Internal Server Error</P>");
473: resp_end_html();
474: }
475:
476: static void
477: resp_search(const struct req *req, struct manpage *r, size_t sz)
478: {
479: size_t i;
480:
481: if (1 == sz) {
482: /*
483: * If we have just one result, then jump there now
484: * without any delay.
485: */
1.2 tedu 486: printf("Status: 303 See Other\r\n");
1.1 schwarze 487: printf("Location: http://%s%s/show/%s/%s?",
488: httphost, scriptname, req->q.manpath, r[0].file);
489: http_printquery(req);
1.2 tedu 490: printf("\r\n"
491: "Content-Type: text/html; charset=utf-8\r\n"
492: "\r\n");
1.1 schwarze 493: return;
494: }
495:
496: qsort(r, sz, sizeof(struct manpage), cmp);
497:
498: resp_begin_html(200, NULL);
499: resp_searchform(req);
500: puts("<DIV CLASS=\"results\">");
501: puts("<TABLE>");
502:
503: for (i = 0; i < sz; i++) {
504: printf("<TR>\n"
505: "<TD CLASS=\"title\">\n"
506: "<A HREF=\"%s/show/%s/%s?",
507: scriptname, req->q.manpath, r[i].file);
508: html_printquery(req);
509: printf("\">");
510: html_print(r[i].names);
511: printf("</A>\n"
512: "</TD>\n"
513: "<TD CLASS=\"desc\">");
514: html_print(r[i].output);
515: puts("</TD>\n"
516: "</TR>");
517: }
518:
519: puts("</TABLE>\n"
520: "</DIV>");
521: resp_end_html();
522: }
523:
524: /* ARGSUSED */
525: static void
526: pg_index(const struct req *req, char *path)
527: {
528:
529: resp_index(req);
530: }
531:
532: static void
533: catman(const struct req *req, const char *file)
534: {
535: FILE *f;
536: size_t len;
537: int i;
538: char *p;
539: int italic, bold;
540:
541: if (NULL == (f = fopen(file, "r"))) {
542: resp_error_badrequest(
543: "You specified an invalid manual file.");
544: return;
545: }
546:
547: resp_begin_html(200, NULL);
548: resp_searchform(req);
549: puts("<DIV CLASS=\"catman\">\n"
550: "<PRE>");
551:
552: while (NULL != (p = fgetln(f, &len))) {
553: bold = italic = 0;
554: for (i = 0; i < (int)len - 1; i++) {
555: /*
556: * This means that the catpage is out of state.
557: * Ignore it and keep going (although the
558: * catpage is bogus).
559: */
560:
561: if ('\b' == p[i] || '\n' == p[i])
562: continue;
563:
564: /*
565: * Print a regular character.
566: * Close out any bold/italic scopes.
567: * If we're in back-space mode, make sure we'll
568: * have something to enter when we backspace.
569: */
570:
571: if ('\b' != p[i + 1]) {
572: if (italic)
573: printf("</I>");
574: if (bold)
575: printf("</B>");
576: italic = bold = 0;
577: html_putchar(p[i]);
578: continue;
579: } else if (i + 2 >= (int)len)
580: continue;
581:
582: /* Italic mode. */
583:
584: if ('_' == p[i]) {
585: if (bold)
586: printf("</B>");
587: if ( ! italic)
588: printf("<I>");
589: bold = 0;
590: italic = 1;
591: i += 2;
592: html_putchar(p[i]);
593: continue;
594: }
595:
596: /*
597: * Handle funny behaviour troff-isms.
598: * These grok'd from the original man2html.c.
599: */
600:
601: if (('+' == p[i] && 'o' == p[i + 2]) ||
602: ('o' == p[i] && '+' == p[i + 2]) ||
603: ('|' == p[i] && '=' == p[i + 2]) ||
604: ('=' == p[i] && '|' == p[i + 2]) ||
605: ('*' == p[i] && '=' == p[i + 2]) ||
606: ('=' == p[i] && '*' == p[i + 2]) ||
607: ('*' == p[i] && '|' == p[i + 2]) ||
608: ('|' == p[i] && '*' == p[i + 2])) {
609: if (italic)
610: printf("</I>");
611: if (bold)
612: printf("</B>");
613: italic = bold = 0;
614: putchar('*');
615: i += 2;
616: continue;
617: } else if (('|' == p[i] && '-' == p[i + 2]) ||
618: ('-' == p[i] && '|' == p[i + 1]) ||
619: ('+' == p[i] && '-' == p[i + 1]) ||
620: ('-' == p[i] && '+' == p[i + 1]) ||
621: ('+' == p[i] && '|' == p[i + 1]) ||
622: ('|' == p[i] && '+' == p[i + 1])) {
623: if (italic)
624: printf("</I>");
625: if (bold)
626: printf("</B>");
627: italic = bold = 0;
628: putchar('+');
629: i += 2;
630: continue;
631: }
632:
633: /* Bold mode. */
634:
635: if (italic)
636: printf("</I>");
637: if ( ! bold)
638: printf("<B>");
639: bold = 1;
640: italic = 0;
641: i += 2;
642: html_putchar(p[i]);
643: }
644:
645: /*
646: * Clean up the last character.
647: * We can get to a newline; don't print that.
648: */
649:
650: if (italic)
651: printf("</I>");
652: if (bold)
653: printf("</B>");
654:
655: if (i == (int)len - 1 && '\n' != p[i])
656: html_putchar(p[i]);
657:
658: putchar('\n');
659: }
660:
661: puts("</PRE>\n"
662: "</DIV>\n"
663: "</BODY>\n"
664: "</HTML>");
665:
666: fclose(f);
667: }
668:
669: static void
670: format(const struct req *req, const char *file)
671: {
672: struct mparse *mp;
673: int fd;
674: struct mdoc *mdoc;
675: struct man *man;
676: void *vp;
677: enum mandoclevel rc;
678: char opts[PATH_MAX + 128];
679:
680: if (-1 == (fd = open(file, O_RDONLY, 0))) {
681: resp_error_badrequest(
682: "You specified an invalid manual file.");
683: return;
684: }
685:
686: mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL,
687: req->q.manpath);
688: rc = mparse_readfd(mp, fd, file);
689: close(fd);
690:
691: if (rc >= MANDOCLEVEL_FATAL) {
692: fprintf(stderr, "fatal mandoc error: %s/%s\n",
693: req->q.manpath, file);
694: resp_error_internal();
695: return;
696: }
697:
698: snprintf(opts, sizeof(opts),
699: "fragment,man=%s/search?sec=%%S&expr=Nm~^%%N$",
700: scriptname);
701:
702: mparse_result(mp, &mdoc, &man, NULL);
703: if (NULL == man && NULL == mdoc) {
704: fprintf(stderr, "fatal mandoc error: %s/%s\n",
705: req->q.manpath, file);
706: resp_error_internal();
707: mparse_free(mp);
708: return;
709: }
710:
711: resp_begin_html(200, NULL);
712: resp_searchform(req);
713:
714: vp = html_alloc(opts);
715:
716: if (NULL != mdoc)
717: html_mdoc(vp, mdoc);
718: else
719: html_man(vp, man);
720:
721: puts("</BODY>\n"
722: "</HTML>");
723:
724: html_free(vp);
725: mparse_free(mp);
726: }
727:
728: static void
729: pg_show(const struct req *req, char *path)
730: {
731: char *sub;
732:
733: if (NULL == path || NULL == (sub = strchr(path, '/'))) {
734: resp_error_badrequest(
735: "You did not specify a page to show.");
736: return;
737: }
738: *sub++ = '\0';
739:
740: /*
741: * Begin by chdir()ing into the manpath.
742: * This way we can pick up the database files, which are
743: * relative to the manpath root.
744: */
745:
746: if (-1 == chdir(path)) {
747: resp_error_badrequest(
748: "You specified an invalid manpath.");
749: return;
750: }
751:
752: if ('c' == *sub)
753: catman(req, sub);
754: else
755: format(req, sub);
756: }
757:
758: static void
759: pg_search(const struct req *req, char *path)
760: {
761: struct mansearch search;
762: struct manpaths paths;
763: struct manpage *res;
764: char **cp;
765: const char *ep, *start;
766: size_t ressz;
767: int i, sz;
768:
769: /*
770: * Begin by chdir()ing into the root of the manpath.
771: * This way we can pick up the database files, which are
772: * relative to the manpath root.
773: */
774:
775: if (-1 == (chdir(req->q.manpath))) {
776: resp_error_badrequest(
777: "You specified an invalid manpath.");
778: return;
779: }
780:
781: search.arch = req->q.arch;
782: search.sec = req->q.sec;
783: search.deftype = TYPE_Nm | TYPE_Nd;
784: search.flags = 0;
785:
786: paths.sz = 1;
787: paths.paths = mandoc_malloc(sizeof(char *));
788: paths.paths[0] = mandoc_strdup(".");
789:
790: /*
791: * Poor man's tokenisation: just break apart by spaces.
792: * Yes, this is half-ass. But it works for now.
793: */
794:
795: ep = req->q.expr;
796: while (ep && isspace((unsigned char)*ep))
797: ep++;
798:
799: sz = 0;
800: cp = NULL;
801: while (ep && '\0' != *ep) {
802: cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
803: start = ep;
804: while ('\0' != *ep && ! isspace((unsigned char)*ep))
805: ep++;
806: cp[sz] = mandoc_malloc((ep - start) + 1);
807: memcpy(cp[sz], start, ep - start);
808: cp[sz++][ep - start] = '\0';
809: while (isspace((unsigned char)*ep))
810: ep++;
811: }
812:
813: if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
814: resp_noresult(req, "You entered an invalid query.");
815: else if (0 == ressz)
816: resp_noresult(req, "No results found.");
817: else
818: resp_search(req, res, ressz);
819:
820: for (i = 0; i < sz; i++)
821: free(cp[i]);
822: free(cp);
823:
824: for (i = 0; i < (int)ressz; i++) {
825: free(res[i].file);
826: free(res[i].names);
827: free(res[i].output);
828: }
829: free(res);
830:
831: free(paths.paths[0]);
832: free(paths.paths);
833: }
834:
835: int
836: main(void)
837: {
838: int i;
839: struct req req;
840: char *querystring, *path, *subpath;
841:
842: /* Scan our run-time environment. */
843:
844: if (NULL == (mandir = getenv("MAN_DIR")))
845: mandir = "/man";
846:
847: if (NULL == (scriptname = getenv("SCRIPT_NAME")))
848: scriptname = "";
849:
850: if (NULL == (cssdir = getenv("CSS_DIR")))
851: cssdir = "";
852:
853: if (NULL == (httphost = getenv("HTTP_HOST")))
854: httphost = "localhost";
855:
856: /*
857: * First we change directory into the mandir so that
858: * subsequent scanning for manpath directories is rooted
859: * relative to the same position.
860: */
861:
862: if (-1 == chdir(mandir)) {
863: fprintf(stderr, "MAN_DIR: %s: %s\n",
864: mandir, strerror(errno));
865: resp_error_internal();
866: return(EXIT_FAILURE);
867: }
868:
869: memset(&req, 0, sizeof(struct req));
870: pathgen(&req);
871:
872: /* Next parse out the query string. */
873:
874: if (NULL != (querystring = getenv("QUERY_STRING")))
875: http_parse(&req, querystring);
876:
877: /*
878: * Now juggle paths to extract information.
879: * We want to extract our filetype (the file suffix), the
880: * initial path component, then the trailing component(s).
881: * Start with leading subpath component.
882: */
883:
884: subpath = path = NULL;
885: req.page = PAGE__MAX;
886:
887: if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
888: req.page = PAGE_INDEX;
889:
890: if (NULL != path && '/' == *path && '\0' == *++path)
891: req.page = PAGE_INDEX;
892:
893: /* Resolve subpath component. */
894:
895: if (NULL != path && NULL != (subpath = strchr(path, '/')))
896: *subpath++ = '\0';
897:
898: /* Map path into one we recognise. */
899:
900: if (NULL != path && '\0' != *path)
901: for (i = 0; i < (int)PAGE__MAX; i++)
902: if (0 == strcmp(pages[i], path)) {
903: req.page = (enum page)i;
904: break;
905: }
906:
907: /* Route pages. */
908:
909: switch (req.page) {
910: case (PAGE_INDEX):
911: pg_index(&req, subpath);
912: break;
913: case (PAGE_SEARCH):
914: pg_search(&req, subpath);
915: break;
916: case (PAGE_SHOW):
917: pg_show(&req, subpath);
918: break;
919: default:
920: resp_error_notfound(path);
921: break;
922: }
923:
924: for (i = 0; i < (int)req.psz; i++)
925: free(req.p[i]);
926: free(req.p);
927: return(EXIT_SUCCESS);
928: }
929:
930: static int
931: cmp(const void *p1, const void *p2)
932: {
933:
934: return(strcasecmp(((const struct manpage *)p1)->names,
935: ((const struct manpage *)p2)->names));
936: }
937:
938: /*
939: * Scan for indexable paths.
940: */
941: static void
942: pathgen(struct req *req)
943: {
944: FILE *fp;
945: char *dp;
946: size_t dpsz;
947:
948: if (NULL == (fp = fopen("manpath.conf", "r")))
949: return;
950:
951: while (NULL != (dp = fgetln(fp, &dpsz))) {
952: if ('\n' == dp[dpsz - 1])
953: dpsz--;
954: req->p = mandoc_realloc(req->p,
955: (req->psz + 1) * sizeof(char *));
956: req->p[req->psz++] = mandoc_strndup(dp, dpsz);
957: }
958: }