]> git.cameronkatri.com Git - mandoc.git/blob - cgi.c
By default, man.cgi should compile with -static (this was unset by mistake).
[mandoc.git] / cgi.c
1 /* $Id: cgi.c,v 1.11 2011/12/07 11:52:36 kristaps Exp $ */
2 /*
3 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/param.h>
22 #include <sys/wait.h>
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <regex.h>
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include "apropos_db.h"
38 #include "mandoc.h"
39 #include "mdoc.h"
40 #include "man.h"
41 #include "main.h"
42 #include "manpath.h"
43
44 #ifdef __linux__
45 # include <db_185.h>
46 #else
47 # include <db.h>
48 #endif
49
50 enum page {
51 PAGE_INDEX,
52 PAGE_SEARCH,
53 PAGE_SHOW,
54 PAGE__MAX
55 };
56
57 struct kval {
58 char *key;
59 char *val;
60 };
61
62 struct req {
63 struct kval *fields;
64 size_t fieldsz;
65 enum page page;
66 };
67
68 static int atou(const char *, unsigned *);
69 static void catman(const char *);
70 static void format(const char *);
71 static void html_print(const char *);
72 static void html_putchar(char);
73 static int kval_decode(char *);
74 static void kval_parse(struct kval **, size_t *, char *);
75 static void kval_free(struct kval *, size_t);
76 static void pg_index(const struct manpaths *,
77 const struct req *, char *);
78 static void pg_search(const struct manpaths *,
79 const struct req *, char *);
80 static void pg_show(const struct manpaths *,
81 const struct req *, char *);
82 static void resp_bad(void);
83 static void resp_baddb(void);
84 static void resp_error400(void);
85 static void resp_error404(const char *);
86 static void resp_begin_html(int, const char *);
87 static void resp_begin_http(int, const char *);
88 static void resp_end_html(void);
89 static void resp_index(const struct req *);
90 static void resp_search(struct res *, size_t, void *);
91 static void resp_searchform(const struct req *);
92
93 static const char *progname;
94 static const char *cache;
95 static const char *host;
96
97 static const char * const pages[PAGE__MAX] = {
98 "index", /* PAGE_INDEX */
99 "search", /* PAGE_SEARCH */
100 "show", /* PAGE_SHOW */
101 };
102
103 /*
104 * This is just OpenBSD's strtol(3) suggestion.
105 * I use it instead of strtonum(3) for portability's sake.
106 */
107 static int
108 atou(const char *buf, unsigned *v)
109 {
110 char *ep;
111 long lval;
112
113 errno = 0;
114 lval = strtol(buf, &ep, 10);
115 if (buf[0] == '\0' || *ep != '\0')
116 return(0);
117 if ((errno == ERANGE && (lval == LONG_MAX ||
118 lval == LONG_MIN)) ||
119 (lval > UINT_MAX || lval < 0))
120 return(0);
121
122 *v = (unsigned int)lval;
123 return(1);
124 }
125
126 static void
127 html_putchar(char c)
128 {
129
130 switch (c) {
131 case ('"'):
132 printf("&quote;");
133 break;
134 case ('&'):
135 printf("&amp;");
136 break;
137 case ('>'):
138 printf("&gt;");
139 break;
140 case ('<'):
141 printf("&lt;");
142 break;
143 default:
144 putchar((unsigned char)c);
145 break;
146 }
147 }
148
149 /*
150 * Print a word, escaping HTML along the way.
151 * This will pass non-ASCII straight to output: be warned!
152 */
153 static void
154 html_print(const char *p)
155 {
156
157 if (NULL == p)
158 return;
159 while ('\0' != *p)
160 html_putchar(*p++);
161 }
162
163 static void
164 kval_free(struct kval *p, size_t sz)
165 {
166 int i;
167
168 for (i = 0; i < (int)sz; i++) {
169 free(p[i].key);
170 free(p[i].val);
171 }
172 free(p);
173 }
174
175 /*
176 * Parse out key-value pairs from an HTTP request variable.
177 * This can be either a cookie or a POST/GET string, although man.cgi
178 * uses only GET for simplicity.
179 */
180 static void
181 kval_parse(struct kval **kv, size_t *kvsz, char *p)
182 {
183 char *key, *val;
184 size_t sz, cur;
185
186 cur = 0;
187
188 while (p && '\0' != *p) {
189 while (' ' == *p)
190 p++;
191
192 key = p;
193 val = NULL;
194
195 if (NULL != (p = strchr(p, '='))) {
196 *p++ = '\0';
197 val = p;
198
199 sz = strcspn(p, ";&");
200 /* LINTED */
201 p += sz;
202
203 if ('\0' != *p)
204 *p++ = '\0';
205 } else {
206 p = key;
207 sz = strcspn(p, ";&");
208 /* LINTED */
209 p += sz;
210
211 if ('\0' != *p)
212 p++;
213 continue;
214 }
215
216 if ('\0' == *key || '\0' == *val)
217 continue;
218
219 /* Just abort handling. */
220
221 if ( ! kval_decode(key))
222 return;
223 if ( ! kval_decode(val))
224 return;
225
226 if (*kvsz + 1 >= cur) {
227 cur++;
228 *kv = mandoc_realloc
229 (*kv, cur * sizeof(struct kval));
230 }
231
232 (*kv)[(int)*kvsz].key = mandoc_strdup(key);
233 (*kv)[(int)*kvsz].val = mandoc_strdup(val);
234 (*kvsz)++;
235 }
236 }
237
238 /*
239 * HTTP-decode a string. The standard explanation is that this turns
240 * "%4e+foo" into "n foo" in the regular way. This is done in-place
241 * over the allocated string.
242 */
243 static int
244 kval_decode(char *p)
245 {
246 char hex[3];
247 int c;
248
249 hex[2] = '\0';
250
251 for ( ; '\0' != *p; p++) {
252 if ('%' == *p) {
253 if ('\0' == (hex[0] = *(p + 1)))
254 return(0);
255 if ('\0' == (hex[1] = *(p + 2)))
256 return(0);
257 if (1 != sscanf(hex, "%x", &c))
258 return(0);
259 if ('\0' == c)
260 return(0);
261
262 *p = (char)c;
263 memmove(p + 1, p + 3, strlen(p + 3) + 1);
264 } else
265 *p = '+' == *p ? ' ' : *p;
266 }
267
268 *p = '\0';
269 return(1);
270 }
271
272 static void
273 resp_begin_http(int code, const char *msg)
274 {
275
276 if (200 != code)
277 printf("Status: %d %s\n", code, msg);
278
279 puts("Content-Type: text/html; charset=utf-8" "\n"
280 "Cache-Control: no-cache" "\n"
281 "Pragma: no-cache" "\n"
282 "");
283
284 fflush(stdout);
285 }
286
287 static void
288 resp_begin_html(int code, const char *msg)
289 {
290
291 resp_begin_http(code, msg);
292
293 puts("<!DOCTYPE HTML PUBLIC " "\n"
294 " \"-//W3C//DTD HTML 4.01//EN\"" "\n"
295 " \"http://www.w3.org/TR/html4/strict.dtd\">" "\n"
296 "<HTML>" "\n"
297 " <HEAD>" "\n"
298 " <META HTTP-EQUIV=\"Content-Type\" " "\n"
299 " CONTENT=\"text/html; charset=utf-8\">" "\n"
300 " <TITLE>System Manpage Reference</TITLE>" "\n"
301 " </HEAD>" "\n"
302 " <BODY>" "\n"
303 "<!-- Begin page content. //-->");
304 }
305
306 static void
307 resp_end_html(void)
308 {
309
310 puts(" </BODY>\n</HTML>");
311 }
312
313 static void
314 resp_searchform(const struct req *req)
315 {
316 int i;
317 const char *expr, *sec, *arch;
318
319 expr = sec = arch = "";
320
321 for (i = 0; i < (int)req->fieldsz; i++)
322 if (0 == strcmp(req->fields[i].key, "expr"))
323 expr = req->fields[i].val;
324 else if (0 == strcmp(req->fields[i].key, "sec"))
325 sec = req->fields[i].val;
326 else if (0 == strcmp(req->fields[i].key, "arch"))
327 arch = req->fields[i].val;
328
329 puts("<!-- Begin search form. //-->");
330 printf("<FORM ACTION=\"");
331 html_print(progname);
332 printf("/search.html\" METHOD=\"get\">\n");
333 puts(" <FIELDSET>\n"
334 " <INPUT TYPE=\"submit\" VALUE=\"Search:\">");
335 printf(" Terms: <INPUT TYPE=\"text\" "
336 "SIZE=\"60\" NAME=\"expr\" VALUE=\"");
337 html_print(expr);
338 puts("\">");
339 printf(" Section: <INPUT TYPE=\"text\" "
340 "SIZE=\"4\" NAME=\"sec\" VALUE=\"");
341 html_print(sec);
342 puts("\">");
343 printf(" Arch: <INPUT TYPE=\"text\" "
344 "SIZE=\"8\" NAME=\"arch\" VALUE=\"");
345 html_print(arch);
346 puts("\">");
347 puts(" </FIELDSET>\n</FORM>\n<!-- End search form. //-->");
348 }
349
350 static void
351 resp_index(const struct req *req)
352 {
353
354 resp_begin_html(200, NULL);
355 resp_searchform(req);
356 resp_end_html();
357 }
358
359 static void
360 resp_error400(void)
361 {
362
363 resp_begin_html(400, "Query Malformed");
364 puts("<H1>Malformed Query</H1>\n"
365 "<P>\n"
366 " The query your entered was malformed.\n"
367 " Try again from the\n"
368 " <A HREF=\"/index.html\">main page</A>\n"
369 "</P>");
370 resp_end_html();
371 }
372
373 static void
374 resp_error404(const char *page)
375 {
376
377 resp_begin_html(404, "Not Found");
378 puts("<H1>Page Not Found</H1>\n"
379 "<P>\n"
380 " The page you're looking for, ");
381 printf(" <B>");
382 html_print(page);
383 puts("</B>,\n"
384 " could not be found.\n"
385 " Try searching from the\n"
386 " <A HREF=\"/index.html\">main page</A>\n"
387 "</P>");
388 resp_end_html();
389 }
390
391 static void
392 resp_bad(void)
393 {
394 resp_begin_html(500, "Internal Server Error");
395 puts("<P>Generic badness happened.</P>");
396 resp_end_html();
397 }
398
399 static void
400 resp_baddb(void)
401 {
402
403 resp_begin_html(500, "Internal Server Error");
404 puts("<P>Your database is broken.</P>");
405 resp_end_html();
406 }
407
408 static void
409 resp_search(struct res *r, size_t sz, void *arg)
410 {
411 int i;
412
413 if (1 == sz) {
414 /*
415 * If we have just one result, then jump there now
416 * without any delay.
417 */
418 puts("Status: 303 See Other");
419 printf("Location: http://%s%s/show/%u/%u.html\n",
420 host, progname,
421 r[0].volume, r[0].rec);
422 puts("Content-Type: text/html; charset=utf-8\n");
423 return;
424 }
425
426 resp_begin_http(200, NULL);
427 puts("<!DOCTYPE HTML PUBLIC " "\n"
428 " \"-//W3C//DTD HTML 4.01//EN\"" "\n"
429 " \"http://www.w3.org/TR/html4/strict.dtd\">" "\n"
430 "<HTML>" "\n"
431 " <HEAD>" "\n"
432 " <META HTTP-EQUIV=\"Content-Type\" " "\n"
433 " CONTENT=\"text/html; charset=utf-8\">" "\n"
434 " <LINK REL=\"stylesheet\" HREF=\"/catman.css\"" "\n"
435 " TYPE=\"text/css\" media=\"all\">" "\n"
436 " <TITLE>System Manpage Reference</TITLE>" "\n"
437 " </HEAD>" "\n"
438 " <BODY>" "\n"
439 "<!-- Begin page content. //-->");
440
441 resp_searchform((const struct req *)arg);
442
443 if (0 == sz)
444 puts("<P>No results found.</P>");
445
446 for (i = 0; i < (int)sz; i++) {
447 printf("<P><A HREF=\"");
448 html_print(progname);
449 printf("/show/%u/%u.html\">", r[i].volume, r[i].rec);
450 html_print(r[i].title);
451 putchar('(');
452 html_print(r[i].cat);
453 if (r[i].arch && '\0' != *r[i].arch) {
454 putchar('/');
455 html_print(r[i].arch);
456 }
457 printf(")</A> ");
458 html_print(r[i].desc);
459 puts("</P>");
460 }
461
462 resp_end_html();
463 }
464
465 /* ARGSUSED */
466 static void
467 pg_index(const struct manpaths *ps, const struct req *req, char *path)
468 {
469
470 resp_index(req);
471 }
472
473 static void
474 catman(const char *file)
475 {
476 FILE *f;
477 size_t len;
478 int i;
479 char *p;
480 int italic, bold;
481
482 if (NULL == (f = fopen(file, "r"))) {
483 resp_baddb();
484 return;
485 }
486
487 resp_begin_html(200, NULL);
488
489 puts("<PRE>");
490 while (NULL != (p = fgetln(f, &len))) {
491 bold = italic = 0;
492 for (i = 0; i < (int)len - 1; i++) {
493 /*
494 * This means that the catpage is out of state.
495 * Ignore it and keep going (although the
496 * catpage is bogus).
497 */
498
499 if ('\b' == p[i] || '\n' == p[i])
500 continue;
501
502 /*
503 * Print a regular character.
504 * Close out any bold/italic scopes.
505 * If we're in back-space mode, make sure we'll
506 * have something to enter when we backspace.
507 */
508
509 if ('\b' != p[i + 1]) {
510 if (italic)
511 printf("</I>");
512 if (bold)
513 printf("</B>");
514 italic = bold = 0;
515 html_putchar(p[i]);
516 continue;
517 } else if (i + 2 >= (int)len)
518 continue;
519
520 /* Italic mode. */
521
522 if ('_' == p[i]) {
523 if (bold)
524 printf("</B>");
525 if ( ! italic)
526 printf("<I>");
527 bold = 0;
528 italic = 1;
529 i += 2;
530 html_putchar(p[i]);
531 continue;
532 }
533
534 /*
535 * Handle funny behaviour troff-isms.
536 * These grok'd from the original man2html.c.
537 */
538
539 if (('+' == p[i] && 'o' == p[i + 2]) ||
540 ('o' == p[i] && '+' == p[i + 2]) ||
541 ('|' == p[i] && '=' == p[i + 2]) ||
542 ('=' == p[i] && '|' == p[i + 2]) ||
543 ('*' == p[i] && '=' == p[i + 2]) ||
544 ('=' == p[i] && '*' == p[i + 2]) ||
545 ('*' == p[i] && '|' == p[i + 2]) ||
546 ('|' == p[i] && '*' == p[i + 2])) {
547 if (italic)
548 printf("</I>");
549 if (bold)
550 printf("</B>");
551 italic = bold = 0;
552 putchar('*');
553 i += 2;
554 continue;
555 } else if (('|' == p[i] && '-' == p[i + 2]) ||
556 ('-' == p[i] && '|' == p[i + 1]) ||
557 ('+' == p[i] && '-' == p[i + 1]) ||
558 ('-' == p[i] && '+' == p[i + 1]) ||
559 ('+' == p[i] && '|' == p[i + 1]) ||
560 ('|' == p[i] && '+' == p[i + 1])) {
561 if (italic)
562 printf("</I>");
563 if (bold)
564 printf("</B>");
565 italic = bold = 0;
566 putchar('+');
567 i += 2;
568 continue;
569 }
570
571 /* Bold mode. */
572
573 if (italic)
574 printf("</I>");
575 if ( ! bold)
576 printf("<B>");
577 bold = 1;
578 italic = 0;
579 i += 2;
580 html_putchar(p[i]);
581 }
582
583 /*
584 * Clean up the last character.
585 * We can get to a newline; don't print that.
586 */
587
588 if (italic)
589 printf("</I>");
590 if (bold)
591 printf("</B>");
592
593 if (i == (int)len - 1 && '\n' != p[i])
594 html_putchar(p[i]);
595
596 putchar('\n');
597 }
598
599 puts("</PRE>\n"
600 "</BODY>\n"
601 "</HTML>");
602
603 fclose(f);
604 }
605
606 static void
607 format(const char *file)
608 {
609 struct mparse *mp;
610 int fd;
611 struct mdoc *mdoc;
612 struct man *man;
613 void *vp;
614 enum mandoclevel rc;
615 char opts[MAXPATHLEN + 128];
616
617 if (-1 == (fd = open(file, O_RDONLY, 0))) {
618 resp_baddb();
619 return;
620 }
621
622 mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
623 rc = mparse_readfd(mp, fd, file);
624 close(fd);
625
626 if (rc >= MANDOCLEVEL_FATAL) {
627 resp_baddb();
628 return;
629 }
630
631 snprintf(opts, sizeof(opts), "style=/man.css,"
632 "man=%s/search.html?sec=%%S&expr=%%N,"
633 /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/,
634 progname);
635
636 mparse_result(mp, &mdoc, &man);
637 vp = html_alloc(opts);
638
639 if (NULL != mdoc) {
640 resp_begin_http(200, NULL);
641 html_mdoc(vp, mdoc);
642 } else if (NULL != man) {
643 resp_begin_http(200, NULL);
644 html_man(vp, man);
645 } else
646 resp_baddb();
647
648 html_free(vp);
649 mparse_free(mp);
650 }
651
652 static void
653 pg_show(const struct manpaths *ps, const struct req *req, char *path)
654 {
655 char *sub;
656 char file[MAXPATHLEN];
657 const char *fn, *cp;
658 int rc;
659 unsigned int vol, rec;
660 DB *idx;
661 DBT key, val;
662
663 if (NULL == path) {
664 resp_error400();
665 return;
666 } else if (NULL == (sub = strrchr(path, '/'))) {
667 resp_error400();
668 return;
669 } else
670 *sub++ = '\0';
671
672 if ( ! (atou(path, &vol) && atou(sub, &rec))) {
673 resp_error400();
674 return;
675 } else if (vol >= (unsigned int)ps->sz) {
676 resp_error400();
677 return;
678 }
679
680 strlcpy(file, ps->paths[vol], MAXPATHLEN);
681 strlcat(file, "/mandoc.index", MAXPATHLEN);
682
683 /* Open the index recno(3) database. */
684
685 idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL);
686 if (NULL == idx) {
687 resp_baddb();
688 return;
689 }
690
691 key.data = &rec;
692 key.size = 4;
693
694 if (0 != (rc = (*idx->get)(idx, &key, &val, 0))) {
695 rc < 0 ? resp_baddb() : resp_error400();
696 goto out;
697 }
698
699 cp = (char *)val.data;
700
701 if (NULL == (fn = memchr(cp, '\0', val.size)))
702 resp_baddb();
703 else if (++fn - cp >= (int)val.size)
704 resp_baddb();
705 else if (NULL == memchr(fn, '\0', val.size - (fn - cp)))
706 resp_baddb();
707 else {
708 strlcpy(file, ps->paths[vol], MAXPATHLEN);
709 strlcat(file, "/", MAXPATHLEN);
710 strlcat(file, fn, MAXPATHLEN);
711 if (0 == strcmp(cp, "cat"))
712 catman(file);
713 else
714 format(file);
715 }
716 out:
717 (*idx->close)(idx);
718 }
719
720 static void
721 pg_search(const struct manpaths *ps, const struct req *req, char *path)
722 {
723 size_t tt;
724 int i, sz, rc;
725 const char *ep, *start;
726 char **cp;
727 struct opts opt;
728 struct expr *expr;
729
730 expr = NULL;
731 cp = NULL;
732 ep = NULL;
733 sz = 0;
734
735 memset(&opt, 0, sizeof(struct opts));
736
737 for (sz = i = 0; i < (int)req->fieldsz; i++)
738 if (0 == strcmp(req->fields[i].key, "expr"))
739 ep = req->fields[i].val;
740 else if (0 == strcmp(req->fields[i].key, "sec"))
741 opt.cat = req->fields[i].val;
742 else if (0 == strcmp(req->fields[i].key, "arch"))
743 opt.arch = req->fields[i].val;
744
745 /*
746 * Poor man's tokenisation.
747 * Just break apart by spaces.
748 * Yes, this is half-ass. But it works for now.
749 */
750
751 while (ep && isspace((unsigned char)*ep))
752 ep++;
753
754 while (ep && '\0' != *ep) {
755 cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *));
756 start = ep;
757 while ('\0' != *ep && ! isspace((unsigned char)*ep))
758 ep++;
759 cp[sz] = mandoc_malloc((ep - start) + 1);
760 memcpy(cp[sz], start, ep - start);
761 cp[sz++][ep - start] = '\0';
762 while (isspace((unsigned char)*ep))
763 ep++;
764 }
765
766 rc = -1;
767
768 /*
769 * Pump down into apropos backend.
770 * The resp_search() function is called with the results.
771 */
772
773 if (NULL != (expr = exprcomp(sz, cp, &tt)))
774 rc = apropos_search
775 (ps->sz, ps->paths, &opt,
776 expr, tt, (void *)req, resp_search);
777
778 /* ...unless errors occured. */
779
780 if (0 == rc)
781 resp_baddb();
782 else if (-1 == rc)
783 resp_search(NULL, 0, (void *)req);
784
785 for (i = 0; i < sz; i++)
786 free(cp[i]);
787
788 free(cp);
789 exprfree(expr);
790 }
791
792 int
793 main(void)
794 {
795 int i;
796 struct req req;
797 char *p, *path, *subpath;
798 struct manpaths paths;
799
800 /* HTTP init: read and parse the query string. */
801
802 progname = getenv("SCRIPT_NAME");
803 if (NULL == progname)
804 progname = "";
805
806 cache = getenv("CACHE_DIR");
807 if (NULL == cache)
808 cache = "/cache/man.cgi";
809
810 if (-1 == chdir(cache)) {
811 resp_bad();
812 return(EXIT_FAILURE);
813 }
814
815 host = getenv("HTTP_HOST");
816 if (NULL == host)
817 host = "localhost";
818
819 memset(&req, 0, sizeof(struct req));
820
821 if (NULL != (p = getenv("QUERY_STRING")))
822 kval_parse(&req.fields, &req.fieldsz, p);
823
824 /* Resolve leading subpath component. */
825
826 subpath = path = NULL;
827 req.page = PAGE__MAX;
828
829 if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
830 req.page = PAGE_INDEX;
831
832 if (NULL != path && '/' == *path && '\0' == *++path)
833 req.page = PAGE_INDEX;
834
835 /* Strip file suffix. */
836
837 if (NULL != path && NULL != (p = strrchr(path, '.')))
838 if (NULL != p && NULL == strchr(p, '/'))
839 *p++ = '\0';
840
841 /* Resolve subpath component. */
842
843 if (NULL != path && NULL != (subpath = strchr(path, '/')))
844 *subpath++ = '\0';
845
846 /* Map path into one we recognise. */
847
848 if (NULL != path && '\0' != *path)
849 for (i = 0; i < (int)PAGE__MAX; i++)
850 if (0 == strcmp(pages[i], path)) {
851 req.page = (enum page)i;
852 break;
853 }
854
855 /* Initialise MANPATH. */
856
857 memset(&paths, 0, sizeof(struct manpaths));
858 manpath_manconf("etc/catman.conf", &paths);
859
860 /* Route pages. */
861
862 switch (req.page) {
863 case (PAGE_INDEX):
864 pg_index(&paths, &req, subpath);
865 break;
866 case (PAGE_SEARCH):
867 pg_search(&paths, &req, subpath);
868 break;
869 case (PAGE_SHOW):
870 pg_show(&paths, &req, subpath);
871 break;
872 default:
873 resp_error404(path);
874 break;
875 }
876
877 manpath_free(&paths);
878 kval_free(req.fields, req.fieldsz);
879
880 return(EXIT_SUCCESS);
881 }